Backtesting and Real use behavioural contrast

Various questions related to programming, testing, deployment and support.

Moderator: admin

Backtesting and Real use behavioural contrast

Postby jgwill » Thu Dec 08, 2022 6:42 pm

Hi,

I require some help with passing from Concept/Backtesting successfully to using it live. When I backtest, my orders gets created, deleted if their stop rate is hit before they become a trade and recreated when a new condition arise.
when I use it live using StrategyRunner, it just creates one entry order and exit the strategy. I am wondering why and maybe I require some code to signify to continue when en event occurs.

I have "simplified" bunch of code bellow to give a simple perspective of what it looks like.

gratitude in advance for the support

Guillaume

Code: Select all

local arrId= 0;
local orders;orders={};
local activeOrder=false;

function ExtUpdate(id, source, period)

    --Which remove the order if the Stop point hits before the entry target
    if #orders > 0 and activeOrder then
        $h!t(arrId, source, period);
    else
        --Some logics to create entry order
        setEntry(...)
    end
end


function setEntry(orderType, _BuySell, _rate, _rateStop, source, period, _mvaa)
   
    -- Create new order (or to replace deleted)
    local valuemap = core.valuemap();
    valuemap.Command = "CreateOrder";
    valuemap.OrderType = orderType;
    valuemap.OfferID = OfferID;
    valuemap.AcctID = Account;
    valuemap.Rate = _rate;
    valuemap.RateStop = _rateStop;
    valuemap.BuySell = _BuySell;
    local _qt = Amount * BaseSize;
    valuemap.Quantity = _qt;


    local success, msg;
    ordering = true;
    vTrace("executing EntryORder");
    success, msg = terminal:execute(102, valuemap);

    if not (success) then
        terminal:alertMessage(instance.bid:instrument(), instance.bid[NOW], "R_FailedCreateLimitOrStop" .. ": " .. msg,
            instance.bid:date(NOW));
    end
end


function ExtAsyncOperationFinished(cookie, success, message, message1, message2)

    --[...]
    if cookie == 102 then -- OrderCreated
        if success then
            vTrace("New Order Created Succesfully ");
           
            orderingContext = getCreatedEntryOrderId();
            activeOrder = true;
           
            --Saving info about the order
            insert_new_order_to_Watch(arrId,...);
            arrId = arrId+1;
        end
    end
end
jgwill
 
Posts: 23
Joined: Tue Aug 30, 2011 9:58 pm
Location: Maliotenam, Canada


Re: Backtesting and Real use behavioural contrast

Postby jgwill » Fri Dec 16, 2022 4:37 am

Code: Select all

function Init()

    strategy:name("xfxcodehelpv2");
    strategy:description("xfxcodehelpv2");
    strategy:type(core.Both);

    strategy:setTag("NonOptimizableParameters", "SendEmail,PlaySound,Email,SoundFile,RecurrentSound,ShowAlert");

    strategy.parameters:addGroup("Price Parameters");

    strategy.parameters:addString("TF", "Time frame ('m1', 'm5', etc.)", "", "m5");
    strategy.parameters:setFlag("TF", core.FLAG_BARPERIODS);

   
    strategy.parameters:addGroup("Parameters");
    strategy.parameters:addDouble("BOP", "Breakout Price", "Price to cross", 1.3333);

    strategy.parameters:addBoolean("BOPFLAG", "Entry on breakout", " triggering signal when breakout occured", false);

   
    strategy.parameters:addInteger("EntryExitTick", "Entry Exit Tick", "Tick to Add to FDB", 2);
    strategy.parameters:addInteger("MaxEntryLossRetry", "Max Loss Entry Retry", "Maximum loss before we stop entering", 12);

   

    strategy.parameters:addString("ALLOWEDSIDE", "Allowed side", "Allowed side for trading or signaling, can be Sell, Buy or Both", "Sell");
    strategy.parameters:addStringAlternative("ALLOWEDSIDE", "Both", "", "Both");
    strategy.parameters:addStringAlternative("ALLOWEDSIDE", "Buy", "", "Buy");
    strategy.parameters:addStringAlternative("ALLOWEDSIDE", "Sell", "", "Sell");

   
    strategy.parameters:addBoolean("PingEachUpdate", "Echo each update", " no desc ", false);

    strategy.parameters:addGroup("Filtering Tolerance Parameters");
    strategy.parameters:addInteger("LipsToleranceFilterP", "lips Pips Tolerance Filter",
                                   "Pips offset to tolerate some overlaps of Lips and Low/High in filtering invalid FDB", 1);
    strategy.parameters:addInteger("TeethToleranceFilterP", "Teeth Pips Tolerance Filter",
                                   "Pips offset to tolerate some overlaps of Teeth and Low/High in filtering invalid FDB", 2);

                                   

    -- ==================== TRADING PARAMS =============
    strategy.parameters:addGroup("Trading Parameters");

    strategy.parameters:addBoolean("AllowTrade", "Allow strategy to trade", "", true);
    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", "", true);
    strategy.parameters:addInteger("Limit", "Limit Order in pips", "", 130, 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", "", true);

    -- ====================  Notification PARAMS =============

    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




local PingEachUpdate;


local h;
h = {};
local o;
o = {};
local a;
a = {};
local w;
w = {};
a.Flag = "None";

local idtbl = {};
h.firstSerieDt = nil;




local orders;
orders = {};

local ctxtbl;
ctxtbl = {};

local actives;
actives = {};
local exitActives;
exitActives = {};

local enteredOrdersPeriod = {};
h.lastOrderEnteredPeriod = -1;








a.useUTC = false;




a.plxdtDefault = "1/1/0001 00:00:00";

local stateSig;
local bsSig;
local oidSig;
local tidSig;
local sigName;
local rSig;
local rsSig;
local dtSig;
local plxSig;
local plxcSig;
local plxdtSig;
local bpSig;
local usegmtSig;
local cPlxcMax,cPlxMax;


local verboseLevel;




-- Parameters block
a.BOP = 0.0;
a.BOPFLAG = false;
a.BOPBROKE = false;

local gSource = nil; -- the source stream

local CID;

-- TODO: Add variable(s) for your indicator(s) if needed
local gBidSource = nil;
local bALLIGATOR = nil;
local tick = nil;

a.pov = "";


-- local Direction;
local CustomID;



a.MaxEntryLossRetry = 1;

a.entryExitTick = 0;



a.AllowTrade = false;

a.OfferData = nil;



h.lastOrderBuySell = "None";
h.lastOrderRate = 0.0;
h.lastOrderRateStop = 0.0;
h.lastOrderLots = 0;
h.lastOrderPeriod = 0;
h.lastOrder_mvaa = 0.0;
h.lastOrderId = nil;
h.lastOrderType = ""
h.lastOrderDT = nil;
h.lastOrderNowATrade = false;
h.lastOrderInvalid = false;


a.lipsToleranceFilter = 0;
a.teethToleranceFilter = 0;
-- local id = {};
local r = {};

local rs = {};
local st = {};
local countLoss = 0;
local countEntry = 0;
local tooManyLooses = false;

local countPassedPeriod = 0;

a.TF = "";

local lastTick;







local pstate = "init";
local pphase = "init";
local ppmstate = "init";
local ppmphase = "init";


function Prepare(nameOnly)
   

   
    a.BOP = instance.parameters.BOP;
    a.BOPFLAG = instance.parameters.BOPFLAG;
   

    verboseLevel = instance.parameters.VerboseLevel; --
   

   
    a.ALLOWEDSIDE = instance.parameters.ALLOWEDSIDE;

    a.entryExitTick = instance.parameters.EntryExitTick;
    a.MaxEntryLossRetry = instance.parameters.MaxEntryLossRetry;

   
    PingEachUpdate = instance.parameters.PingEachUpdate;

    a.TF = instance.parameters.TF;
    a.pov = instance.bid:instrument() .. "_" .. instance.parameters.TF;
   

    local name = profile:id() .. "__" .. a.pov .. " : "; -- ..

    instance:name(name);

    if nameOnly then
        return;
    end

    ShowAlert = instance.parameters.ShowAlert;

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

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

    a.useUTC = instance.parameters.UseUTC;
    a.AllowTrade = instance.parameters.AllowTrade;
    local msgAllowedTrade = "Strategy Can not TRADE";
    if a.AllowTrade then
        msgAllowedTrade = "Strategy can TRADE";
    end

    if a.AllowTrade then
        a.Account = instance.parameters.Account;
        -- core.host:trace("Account: " .. Account);
        a.Amount = instance.parameters.Amount;
        a.BaseSize = core.host:execute("getTradingProperty", "baseUnitSize", instance.bid:instrument(), a.Account);
        -- BaseSize = 1000; -- BYPASSING the 10K which is a lot for now

        a.OfferData = core.host:findTable("offers"):find("Instrument", instance.bid:instrument());
        a.instrument = a.OfferData.Instrument;
        a.ContractSize = a.OfferData.ContractSize;
        a.PointSize = a.OfferData.PointSize;

        a.OfferID = a.OfferData.OfferID;

        a.CanClose = core.host:execute("getTradingProperty", "canCreateMarketClose", instance.bid:instrument(), a.Account);

        a.BaseSize = core.host:execute("getTradingProperty", "baseUnitSize", a.instrument, a.Account);
        core.host:trace("---- DEBUG :: BaseSize: " .. a.BaseSize);

        a.SetLimit = instance.parameters.SetLimit;
        a.Limit = instance.parameters.Limit * instance.bid:pipSize();
        a.SetStop = instance.parameters.SetStop;
        a.Stop = instance.parameters.Stop * instance.bid:pipSize();
        a.TrailingStop = instance.parameters.TrailingStop;
    end
   
    if a.BaseSize == -1 then
        core.host:trace("BaseSize == -1, manual fix to :1000");
        a.BaseSize = 1000;
    end
    a.qt2trade = a.Amount * a.BaseSize;

   

    a.PipCost = 0.00;
    a.PipCost = core.host:findTable("offers"):find("Instrument", instance.bid:instrument()).PipCost;
   

   
    gBidSource = ExtSubscribe(1, nil, instance.parameters.TF, true, "bar"); -- Bid/Sell
    --  gAskSource = ExtSubscribe(2, nil, instance.parameters.TF, false, "bar");  -- Ask/Buy
    if a.ALLOWEDSIDE == "Sell" then
        tick = ExtSubscribe(3, nil, "t1", true, "close");
    else
        tick = ExtSubscribe(3, nil, "t1", false, "close");
    end

    ExtSetupSignal("FDBES::", ShowAlert);
    --    gBidSource = core.host:execute("getHistory", 1, instance.bid:instrument(), timeframe, 0, 0, true);
    --    gAskSource = core.host:execute("getHistory", 2, instance.bid:instrument(), timeframe, 0, 0, false);

    -- CloseSource = ExtSubscribe(2, nil, instance.parameters.TF, true, "close");
    a.Precision = gBidSource:getPrecision();
    a.Pipsize = instance.bid:pipSize();

    a.lipsToleranceFilter = a.Pipsize * instance.parameters.LipsToleranceFilterP; -- TODO Eval if Params
    a.teethToleranceFilter = a.Pipsize * instance.parameters.TeethToleranceFilterP; -- TODO Eval if
   

   

    bALLIGATOR = core.indicators:create("ALLIGATOR", gBidSource, 13, 8, 8, 5, 5, 3, "SMMA");

   

    cnf_accounts();
   
end






function cnf_accounts()
    local enum, row;
    enum = core.host:findTable("accounts"):enumerator();
    row = enum:next();
    local c = 0;
    local _msg = "";

    while (row ~= nil) do
        core.host:trace(row.AccountID);
        a.AccountID = row.AccountID;
        _msg = _msg .. a.AccountID .. ", ";
        core.host:trace(row.AccountName);
        core.host:trace(row.Balance);
        core.host:trace(row.Kind);
        -- core.host:trace(row.    );
        core.host:trace(row.GrossPL);
        c = c + 1;
        row = enum:next();
    end
    if c > 1 then
        core.host:trace("WARNING:: More than one ACcount was found, might want to choose which ..." .. _msg)
    end
end







local curTradeId = nil;
local curTradeOpenPosition = 0.0;
local curTradeDtOpen;
local curPLinPips = 0.0;
local curPLinPipsMax = 0.0;
local curGrosPL = 0.0;
local curGrosPLMax = 0.0;

local curCom = 0;
local curTradeRow = nil;

function printCurTrade()

    if curTradeId ~= nil then
        core.host:trace("-------------------------------------");
        core.host:trace("===== TradeID: " .. curTradeId);
        core.host:trace("======== PL/$: " .. curPLinPips .. "/" .. curGrosPL);
        core.host:trace("======== Dt/Open: " .. curTradeDtOpen .. "/" .. curTradeOpenPosition);
        core.host:trace("======== Com: " .. curCom);
        --    core.host:trace("======== Com: " .. cur );

        core.host:trace("---------------------------------------");
    end
end


function printTrade(arrId)
    local t = orders[arrId];
    if t ~= nil and t.tid ~= nil and t.tid ~= "-1" then
        core.host:trace("-------------------------------------");
        core.host:trace("===== TradeID: " .. t.tid);
        core.host:trace("======== PL/$: " .. t.plx .. "/" .. t.plxc);
        core.host:trace("======== Dt/Open: " .. t.tdt .. "/" .. t.to);

        core.host:trace("======== Com: " .. t.tc);
        --    core.host:trace("======== Com: " .. cur );

        core.host:trace("---------------------------------------");
    end
end



local stopStrategy = false; -- Would define when Strategy Stops

local ordering = false;
local deleting = false;
local gettingTradeInformation = false;
local _once = true;




function ExtUpdate(id, source, period)

    local p;
    p = {};
    if a.AllowTrade then
        if not (checkReady("trades")) or not (checkReady("orders")) then
            return;
        end
    end
   

   

    -- ===============================Tick UPDATE =>>>======================--
    -- ============ Used to follow trade and order and act on it.
    if id == 3 then
        lastTick = source[period];
       
        if PingEachUpdate then
            local __dt = getUTCDate(source, period);
            core.host:trace("id:" .. id .. ", period:" .. period .. __dt);
        end
       
    end

   
    if tooManyLooses == true then
        core.host:trace("-- TOO Many Looses, we are Done---");
        return; -- we are DONE
    end

   

    if id == 3 then
       
        local arrId = 0;

       
        lastTick = source[period];
       
        while arrId < countEntry and #orders > 0 do -- @STCGoal Each orders is iterated so we get a traget value foreach
            local t = orders[arrId];
           
            local _latestState2 = t.state;
            if actives[arrId] then
                if _latestState2 == "trading" then
                    validateCurrentTrading2(arrId, source, period);
                elseif _latestState2 ~= "deleted" and _latestState2 ~= "deleting" and _latestState2 ~= "invalid/deleting" and _latestState2 ~= "invalid/deleted" and
                    _latestState2 ~= "invalid" and _latestState2 ~= "deleting" and _latestState2 ~= "loss" and _latestState2 ~= "profit" then
                    -- and lastOrderNowATrade == false then
                    validateCreatedEntryOrderId2(arrId, source, period);
                end
            end
            arrId = arrId + 1;
        end
    end
    -- ===============================Tick UPDATE =<<<======================--
    -- ======================================================================--

    -- ======================================================================--
    -- ===============================Timeframe UPDATE =======================--

    if id == 1 then

        bALLIGATOR:update(core.UpdateLast);
       

        p.dt = getUTCDate(source, period);
        local _hadAnySignal = false;

        core.host:trace(p.dt .. " ::period: " .. period .. " -Last tick: " .. lastTick);
           

        if not stopStrategy and period > 40 then

            p.dtnbL = source:date(period)
           
            p.dtL = core.formatDate(p.dtnbL)
           
            local i;
            i = {};
            i.lips = bALLIGATOR.Lips[period];
            i.teeth = bALLIGATOR.Teeth[period];
            i.jaw = bALLIGATOR.Jaw[period];
           

            p.cH = source.high[period];
            p.cL = source.low[period];
            p.cC = source.close[period];
            p.cO = source.open[period];
            p.cM = ((p.cH + p.cL) / 2);
            p.pH = source.high[period - 1];
            p.pL = source.low[period - 1];
            p.rB = p.cH - p.cL; -- the risk of the bar

           
            -- BOP
            if a.ALLOWEDSIDE == "Sell" then
                -- BOP is from up to down
                if p.cL < a.BOP then
                    if a.BOPBROKE == false then -- trigger signal once
                        ExtSignal(source, period, "Sell BOP Broke", a.SoundFile, a.Email, a.RecurrentSound);
                    end
                    a.BOPBROKE = true;
                end
            elseif a.ALLOWEDSIDE == "Buy" then
                -- BOP is from down to up
                if p.cH > a.BOP then
                    if a.BOPBROKE == false then -- trigger signal once
                        ExtSignal(source, period, "Buy BOP Broke", a.SoundFile, a.Email, a.RecurrentSound);
                    end
                    a.BOPBROKE = true;
                end
            end

           
           
            local _weCanCreateMoreEntry = true;

           
            --------------------FDB-------   
            -- IMPORT BDB
            local isBDB = 0;
            local isExtreme = 0;
            local done = 0;
            local bT = 0; -- Bar Type eg.  Extreme : 11 or 33 , climber/drifter: 13, 31 or Normal : 22

            ----------------------------------------------------------BDB BEARISH------
            -- check if BEARISH
            if (p.cH > p.pH and p.cC < p.cM and (p.cL - i.lips + a.lipsToleranceFilter) > 0 and (p.cL - i.teeth + a.teethToleranceFilter) > 0 and (p.cL - i.jaw) >
                0) then

                isBDB = -1;

                -- Find Extreme Bar
                local ox = p.cO - p.cL; -- Open - Low
                local cx = p.cC - p.cL; -- close - low
                local ck = (p.cH - p.cL) / 3; -- value of 1/3 of the bar height
                if (ox < ck and cx < ck) then

                    -- XUP:set(period, source.low[period], "\118", source.low[period]);
                    isExtreme = -11;
                end

               

                done = 1;
               

            end

            ----------------------------------------------------------BDB BULLISH------
            -- check if BULLISH
            if (p.cL < p.pL and p.cC > p.cM and (i.lips - p.cH + a.lipsToleranceFilter) > 0 and (i.teeth - p.cH + a.teethToleranceFilter) > 0 and (i.jaw - p.cH) >
                0) then
                isExtreme = 0;

                -- Find Extreme Bar
                local ox = p.cH - p.cO; -- Open
                local cx = p.cH - p.cC; -- close
                local ck = (p.cH - p.cL) / 3; -- value of 1/3 of the bar height
                if ((ox < ck) and (cx < ck)) then                   
                   
                    isExtreme = 33;
                end
                isBDB = 1;

            end

            -- If not a BDB
            if (isBDB == 0) then

            else

               
                -- validate FDB Using AO
                local fdbtxt = "null";
               


                if _weCanCreateMoreEntry then
                   

                        ExtSignal(source, period, "FDB " .. fdbtxt .. p.cH .. "|" .. p.cL, a.SoundFile);
               
                        fdbEntering(isBDB, p, source, period, i); -- We order on the right side

                end -- we can create more entry
            end -- We have an FDB

        else
            core.host:trace("Not enough period yet " .. period);
        end -- Above 34 periods

    end -- if source id is 1 (Bar)
end


function getEntryExitTickValue()
    return (a.Pipsize * a.entryExitTick / 10);
end




function fdbEntering(fdbValue, p, source, period, i)

    local doEnter = true;
    if a.BOPFLAG and not a.BOPBROKE then
        doEnter = false;
        core.host:trace(period .. " " .. " FDB Entry Canceled - BREAKOUT Did not yet OCcured");
    end

    -- if canCreateMoreEntryOrder() == true then
    if doEnter then
        core.host:trace("------------FDB Entering-----------------");
        fdbCreateEntry(fdbValue, p, source, period, i);
    end
    -- end
end



function fdbCreateEntry(fdbValue, p, source, period, i)
    local buysell = "B";
    local entryRate = p.cH + getEntryExitTickValue();
    local exitRate = p.cL - getEntryExitTickValue(); -- default to BUY
    local barDT = getUTCDate(source, period);
    local dt = getUTCDate(source, period);
  local val1;local val2;
  val1=entryRate;
  val2=exitRate;
    if (fdbValue < 0) then
        buysell = "S";
        entryRate = p.cL - getEntryExitTickValue();
        exitRate = p.cH + getEntryExitTickValue();
        val2=entryRate;
        val1=exitRate;
    end
    h.riskVal = toShorten((((val1 - val2) / a.Pipsize) ), 2);
    h.riskMoneyPC = h.riskVal * a.Pipsize;

   

   

    local campaignSide = false;

    if a.ALLOWEDSIDE == "Both" then
        campaignSide = true;
    end
    if a.ALLOWEDSIDE == "Sell" and buysell == "S" then
        campaignSide = true;
    end
    if a.ALLOWEDSIDE == "Buy" and buysell == "B" then
        campaignSide = true;
    end
   

    if campaignSide then
        core.host:trace(period .. " " .. dt .. "::  Creating Entry:" .. buysell .. ", Rate:" .. entryRate .. " , exit: " .. exitRate);
        h.lastOrderDT = barDT;
        h.lastOrderEnteredPeriod = period;
        h.twinToOrder =false;
       
        setEntry("SE", buysell, entryRate, exitRate, source, period, i,false);
    else
        core.host:trace(period .. "                "   .. "  ---NOT CAMPAIGN SIde, SKIPPED");
    end

end


-- Adapted from Fractal Alligator System...
function setEntry(orderType, _BuySell, _rate, _rateStop, source, period, i,isATwinOrder)
    core.host:trace(period .. "      setEntry(: " .. orderType .. " - BuySell:" .. _BuySell .. ", Rate : " .. _rate .. " ExitRate: " .. _rateStop);
   


    -- Create new order (or to replace deleted)
    local valuemap = core.valuemap();
    local _featureCOMPLETED22121015= false;
   
   
    valuemap.Command = "CreateOrder";
    valuemap.OrderType = orderType;
    valuemap.OfferID = a.OfferID;
    valuemap.AcctID = a.Account;
    valuemap.Rate = _rate;
    valuemap.RateStop = _rateStop;
    valuemap.BuySell = _BuySell;
    local _qt = a.Amount * a.BaseSize;
    valuemap.Quantity = _qt;
    h.lastOrderLots = _qt;
    h.lastOrderType = orderType;
    h.lastOrderRate = _rate;   
    h.lastOrderRateStop = _rateStop;
    h.lastOrderBuySell = _BuySell;
    h.lastOrderPeriod = period;

   
    local rLimit = a.Limit;
    local aSetLimit = a.SetLimit;
   
    if aSetLimit then
        --core.host:trace("LIMIT: " .. a.Limit);
        -- set limit order
        local tLimit =  _rate - a.Limit;
        if _BuySell == "B" then
             tLimit = _rate + a.Limit;
        end
        valuemap.RateLimit = tLimit;
       
    end

   

    if (not a.CanClose) and (a.SetStop or aSetLimit) then
        valuemap.EntryLimitStop = 'Y'  ;
    end

    local success, msg;
    ordering = true;
    core.host:trace(period .. "   executing EntryORder  "  );
    success, msg = terminal:execute(102, valuemap);

    if not (success) then
        terminal:alertMessage(instance.bid:instrument(), instance.bid[NOW], "R_FailedCreateLimitOrStop" .. ": " .. msg, instance.bid:date(NOW));
    end
end





function deleteEntryOrder2(arrId, source, period)
    local _orderId = orders[arrId].oid;
    local _plxdt = getUTCDate(source, period);
    local enum, row;
   
    core.host:trace("===================================================================");
    core.host:trace("DELETING ORDER (invalidated)-----------------------------------" .. _orderId .. " <<<<<");
    core.host:trace("===================================================================");

    enum = core.host:findTable("orders"):enumerator();
    row = enum:next();

    while (row ~= nil) do
        if row.OrderID == _orderId then -- nonOCO order,
            break
        end
        row = enum:next();
    end
    if (row ~= nil) then
        -- Or delete order in other case
        local valuemap = core.valuemap();

        valuemap.Command = "DeleteOrder"
        valuemap.OrderID = row.OrderID

        setOrderState(arrId, "deleting");

        orders[arrId].plxdt = _plxdt;

        actives[arrId] = false;
       

       

        local success, msg = terminal:execute(133, valuemap);

        if not (success) then
            terminal:alertMessage(instance.bid:instrument(), instance.bid[NOW], "DeleteOrder" .. ": " .. msg, instance.bid:date(NOW));
        else
            setOrderState(arrId, "invalid");
           

        end
    end
end


function updateEntryOrderBecomeTrade2(arrId, source, period)
    gettingTradeInformation = true;
    local t = orders[arrId];
    local _orderId = t.oid;
    local pTid = t.tid;
    local pPLinPips = t.plx;
    local pPLinCash = t.plxc;

    local curDt = getUTCDate(source, period);
    local _plxdt = curDt;

    local enum, row;
    enum = core.host:findTable("trades"):enumerator();
    row = enum:next();
    while (row ~= nil) do
        if row.OpenOrderID == _orderId then -- nonOCO order,
            break
        end
        row = enum:next();
    end

    if (row ~= nil) then

        local _requireUpdate = false;
        local _tid = row.TradeID;
        local _cPlx = toMoneyNumber(row.PL);
        local _ppl = 0.0;
        local _cPlxc = toMoneyNumber(row.GrossPL);

        if _tid ~= pTid then
            _requireUpdate = true; -- First time we get data for the trade
            cPlxcMax = _cPlxc;
            cPlxMax = _cPlx;
            orders[arrId].tid = _tid;
            orders[arrId].tdt = curDt; -- row.Time;
            orders[arrId].to = toInstrumentPrecisionNumber(row.Open);
            orders[arrId].tm = toMoneyNumber(row.Com);
        else
            _ppl = pPLinPips;
            if _ppl < _cPlx then
                _requireUpdate = true;
                cPlxMax = _cPlx;
                cPlxcMax = _cPlxc;
            end
        end
        -- string.format("%.2f", curr);
        local _curCom = row.Com;

        if _requireUpdate then

            orders[arrId].plx = toMoneyNumber(cPlxMax);
            orders[arrId].plxdt = _plxdt;
            orders[arrId].plxc = toMoneyNumber(cPlxcMax);
            orders[arrId].tm = toMoneyNumber(_curCom);

           

        end

    else

        updateEntryClosedTrade2(arrId, source, period);

        curPLinPips = 0;
        curGrosPL = 0;
        curCom = 0;
        curTradeId = nil;
        curTradeDtOpen = nil;
        curTradeOpenPosition = 0.0;
    end

    gettingTradeInformation = false;
end


function updateEntryClosedTrade2(arrId, source, period)

    local dtClosed = getUTCDate(source, period);
    local t = orders[arrId];
    local _orderId = t.oid;
    local _plx = t.plx;
    local tid = t.tid;
    local enum, row;
    enum = core.host:findTable("Closed Trades"):enumerator();
    row = enum:next();
    while (row ~= nil) do
        if row.TradeID == tid then -- nonOCO order,
            break
        end
        row = enum:next();
    end

    if (row ~= nil) then

        actives[arrId] = false;

        orders[arrId].tc = toInstrumentPrecisionNumber(row.Close);
        -- local _tc = row.Close;
        orders[arrId].tpl = toMoneyNumber(row.PL);
        if row.PL > 0 then
            orders[arrId].state = "profit";
            orders[arrId].bp = period;
        elseif row.PL < 0 then
            orders[arrId].state = "loss";
            if t.plx > t.riskp then
                orders[arrId].state2 = "twinwin";
            end
        end
        orders[arrId].tplc = toMoneyNumber(row.GrossPL);
        orders[arrId].tdt = dtClosed; -- row.Time;
        orders[arrId].tm = toMoneyNumber(row.Com);

       

    end

end








function getCreatedEntryOrderId(orderType, _BuySell, _rate, _rateStop)
    -- try to find order in specified direction
    local enum, row;
    enum = core.host:findTable("orders"):enumerator();
    row = enum:next();
    while (row ~= nil) do
        if row.OfferID == a.OfferID and row.AccountID == a.AccountID and row.BS == _BuySell and row.Type == orderType and row.Rate == _rate and row.Stop ==
            _rateStop and row.ContingencyType == 0 then -- nonOCO order,
            break
        end
        row = enum:next();
    end

    if (row ~= nil) then
        -- exit if Order exist with the same rate
        if (row.Rate == _rate) then
         
           
            h.lastOrderId = row.OrderID;

        end
    end
end


-- Validate if RateSTop of LastOrderID had Broke (means INVALID ENTRY to Delete)

local traceEveryFreq = 10;
local countTrace = 0;

function validateCurrentTrading2(arrId, source, period)

    local orderType, _bs, _rate, _rateStop, oid;
    local t = orders[arrId];

    _bs = t.bs;
    _rate = t.r;
    _rateStop = t.rs;
    oid = t.oid;

    local cH = source[period]; -- tick we use close
    local cL = source[period];
    local _loss = false;
    local validationFormula = "";
    if _bs == "S" then
        validationFormula = " S:  rateStop: " .. _rateStop .. "<" .. cH;
        if _rateStop < cH then
            _loss = true;
        end
    elseif _bs == "B" then
        validationFormula = " B:  rateStop: " .. _rateStop .. ">" .. cL;
        if _rateStop > cL then
            _loss = true;
        end
    end

    if _loss == true then
        setOrderState(arrId, "loss");

        countLoss = countLoss + 1;
        local _msg = oid .. " BECAME a LOSS :(  total loss/max: " .. countLoss .. "/" .. a.MaxEntryLossRetry;
        updateEntryClosedTrade2(arrId, source, period);
        ExtSignal(source, period, _msg, a.SoundFile, a.Email, a.RecurrentSound);
        core.host:trace(_msg);
        countTrace = 0;
        --  sig_Save_Table2();
    else

        updateEntryOrderBecomeTrade2(arrId, source, period);

    end

end


function validateCreatedEntryOrderId2(arrId, source, period)
    local t = orders[arrId];
    local oid = t.oid;
    local oidt = t.dt;

    local _lState = t.state;

    if _lState == nil or _lState == "deleted" or _lState == "invalid" then
        core.host:trace("               DELETED ENTRY DONT REQUIRE VALIDATION : " .. arrId);

    else
        local cH = source[period]; -- tick validation
        local cL = source[period];

        local barDT = getUTCDate(source, period);
        local msg = "";
        local lastOrderMETA = " OrderID: " .. oid .. " created : " .. oidt;
        local validationFormula = "";
        local _lastOrderNowATrade = false;
        if t.bs == "S" then
            validationFormula = " S: " .. t.r .. ">" .. cL .. " else rateStop: " .. t.rs .. "<" .. cH;

            if t.r > cL then -- We now Have a Trade
                local tmsg = "   NOW TRADING  " .. t.bs .. " at: " .. barDT .. validationFormula;
                setOrderState(arrId, "trading");

                _lastOrderNowATrade = true;

            elseif t.rs < cH then -- and not lastOrderNowATrade
                local tmsg = "Previous OrderID:" .. oid .. " Created: " .. oidt .. " INVALIDATED at : " .. barDT .. validationFormula;

                setOrderState(arrId, "invalid");

                -- lastOrderInvalid = true;
                deleteEntryOrder2(arrId, source, period);

            end
        elseif t.bs == "B" then
            validationFormula = " B: " .. t.r .. "<" .. cH .. " else rateStop: " .. t.rs .. ">" .. cL;
            if t.r < cH then -- We now Have a Trade
                local tmsg = "   NOW TRADING  " .. t.bs .. " at: " .. barDT .. validationFormula;
                setOrderState(arrId, "trading");
                _lastOrderNowATrade = true;

               
            elseif t.rs > cL then -- and not lastOrderNowATrade
                local tmsg = "Previous OrderID:" .. h.lastOrderId .. h.lastOrderBuySell .. " Created: " .. h.lastOrderDT .. " INVALIDATED at : " .. barDT ..
                                 validationFormula;
                setOrderState(arrId, "invalid");
                deleteEntryOrder2(arrId, source, period);

            end

        end

        if _lastOrderNowATrade == true then --
            core.host:trace("---------------------------------------");
            core.host:trace("------CAMPAIGN STARTED " .. h.lastOrderBuySell .. validationFormula);
            countTrace = 0;
            updateEntryOrderBecomeTrade2(arrId, source, period);

        end
    end

end





function traceSignal(sigDt, sigTag, sigDir)
    core.host:trace(sigDt .. "," .. sigTag .. "," .. sigDir);
end


function getUTCDate(source, period)
    --    if source == nil then return plxdtDefault end

    local date = source:date(period);
    if a.useUTC then
        date = core.host:execute("convertTime", 1, 2, source:date(period));
    end
    local _dt = core.formatDate(date)
    return _dt
end


function canCreateMoreEntryOrder(source, period)
    local _state = getStateOfLatestOrder();
    if _state == nil or _state == "deleted" or _state == "loss" or _state == "profit" then
        if countLoss > a.MaxEntryLossRetry then
            tooManyLooses = true;
            ExtSignal(source, period, " TOO MANY LOOSES:" .. countLoss .. ">" .. a.MaxEntryLossRetry);
            return false;
        end
        return true;
    else
        return false;
    end
end





function getBOOLValueAsString(_boolValue)
    if _boolValue then
        return "1"
    else
        return "0"
    end
end








function ExtAsyncOperationFinished(cookie, success, message, message1, message2)
    local _viewMlg = cookie .. " ";
    if message ~= nil then
        _viewMlg = _viewMlg .. " 0:" .. message;
    end
   
    if message1 ~= nil then
        _viewMlg = _viewMlg .. " ,1:" .. message1;
    end
   
    if message2 ~= nil then
        _viewMlg = _viewMlg .. " ,2:" .. message2;
    end

    core.host:trace("==============_viewMlg================");
    core.host:trace(_viewMlg);
    core.host:trace("==============_viewMlg================");

    if cookie == 200 then
        core.host:trace("Stop changed completed ");
    elseif cookie == 133 then -- DeleteOrder
        core.host:trace("Invalid order deleted============================================================Invalid order deleted ");
       
    elseif cookie == 102 then -- OrderCreated
        if success then
            core.host:trace("New Order Created Succesfully ");
           
            getCreatedEntryOrderId(h.lastOrderType, h.lastOrderBuySell, h.lastOrderRate, h.lastOrderRateStop);
            h.lastOrderNowATrade = false;

            local _state = "created";
            local _r = h.lastOrderRate;
           
            local _rs = h.lastOrderRateStop;
            local _rsx = _rs; -- for now
            local _oid = h.lastOrderId;
            local _tid = "-1"; -- not a trade at thattime
            local _dt = h.lastOrderDT;
            local _plxdt = a.plxdtDefault; -- January 1, 0001 00:00:00
            local _plx = 0;
            local _plxc = 0;
            local _bs = "S";
            local _bp = h.lastOrderPeriod;
            local _riskp = h.riskVal;
            if _r > _rs then
                _bs = "B";
            end
           
            sig_insertNewEntry2(countEntry, _oid, _tid, _state, _bs, _r, _rs, _dt, _plx, _plxc, _plxdt, _bp, 0, _rsx,_riskp);
           
            st[h.lastOrderId] = _state;
            r[h.lastOrderId] = h.lastOrderRate;
            rs[h.lastOrderId] = h.lastOrderRateStop;
            idtbl[countEntry] = h.lastOrderId;
            countEntry = countEntry + 1;
            core.host:trace("LastID: " .. getLastRecordedOrderId());
           
           
            core.host:trace(h.lastOrderId);
        else
            core.host:trace("New Order Creation FAILED -->" .. message);

        end

       

    -- elseif cookie == 1001 then
    --     print("order: %s %s %s", message, message1, message2);
    -- elseif cookie == 1002 then
    --     print("trade: %s %s %s", message, message1, message2);
    end

    ordering = false;
end





--- func sig_NewEntry
---@param _entryCount any
---@param _oid any
---@param _tid any
---@param _state any
---@param _bs any
---@param _r any
---@param _rs any
---@param _dt any
---@param _plx any
---@param _plxc any
---@param _plxdt any
---@param _bp any
---@param _sig any
---@param _rsx any
---@param _mvaa any
---@param _riskp any
function sig_NewEntry(_entryCount, _oid, _tid, _state, _bs, _r, _rs, _dt, _plx, _plxc, _plxdt, _bp, _sig, _rsx,_riskp)

    local t = {};
    t['oid'] = _oid;
    t['tid'] = _tid;
    t['tdt'] = a.plxdtDefault;
    t['to'] = 0.0;
    t['tc'] = 0.0;
    t['tm'] = 0.00;
    t['tpl'] = 0.0;
    t['tplc'] = 0.00;
    t['state'] = _state;
    t['bs'] = _bs;
    t['r'] = toInstrumentPrecisionNumber(_r);
    t['rs'] = toInstrumentPrecisionNumber(_rs);
    t['rsx'] = toInstrumentPrecisionNumber(_rsx);
    t['ro'] = 0.0;   
    t['dt'] = _dt;
    t['plx'] = _plx;
    t['riskp'] = _riskp;
    t['plxc'] = _plxc;
    t['plxdt'] = _plxdt;
    t['bp'] = _bp;
    t['sig'] = _sig;
    t['i'] = a.instrument;
    t['tf'] = a.TF;
    t['usegmt'] = a.useUTC;
    t['tollipst'] = a.lipsToleranceFilter;
    t['tolteeth'] = a.teethToleranceFilter;
    return t;
end


--- Insert a new entry in the order history
---@param _entryCount integer
---@param _oid string
---@param _tid string
---@param _state string
---@param _bs string
---@param _r number
---@param _rs number
---@param _dt string
---@param _plx number
---@param _plxc number
---@param _plxdt os.date
---@param _bp number
---@param _sig string
---@param _rsx number
function sig_insertNewEntry2(_entryCount, _oid, _tid, _state, _bs, _r, _rs, _dt, _plx, _plxc, _plxdt, _bp, _sig, _rsx,_riskp)

    local t = sig_NewEntry(_entryCount, _oid, _tid, _state, _bs, _r, _rs, _dt, _plx, _plxc, _plxdt, _bp, _sig, _rsx,_riskp);

    actives[countEntry] = true;
    exitActives[countEntry] = 0;
    enteredOrdersPeriod[countEntry] = h.lastOrderEnteredPeriod;

    sig_AddTableEntryToStore(t);

   

    tidSig = _tid;
    oidSig = _oid;
    bsSig = _bs;
    stateSig = _state;
    rSig = _r;
    rsSig = _rs;
    dtSig = _dt;
    plxSig = _plx;
    plxdtSig = _plxdt;
    bpSig = _bp;
    sigName = _sig;
end


function sig_AddTableEntryToStore(t)
    orders[countEntry] = t;
    -- sig_json_Save_Table_to_File2(orders);
end




function postOrderCreatedRegistration() -- NOT YET IN USE
    getCreatedEntryOrderId(h.lastOrderType, h.lastOrderBuySell, h.lastOrderRate, h.lastOrderRateStop);
    newOrderToFollow(h.lastOrderId, h.lastOrderRate, h.lastOrderRateStop);
end




function setStateOfLatestOrder(_state)
    local curEntryId = countEntry - 1;
    setOrderState(curEntryId, _state);
    orders[curEntryId].state = _state;
end


function getStateOfLatestOrder()
    local _orderId = getLastRecordedOrderId();

    if (_orderId == nil) then
        return nil;
    else
        return st[_orderId];
    end
end


function setOrderState(_cnt, _state)
    local _orderId = idtbl[_cnt];
    st[_orderId] = _state;
    orders[_cnt].state = _state;
end


function getOrderState(_cnt)
    return orders[_cnt].state;
end


function printLastRecordedOrder()
    local _orderId = getLastRecordedOrderId();
    printOrder(_orderId);
end


function printOrder(_orderId)
    if _orderId == nil then
        core.host:trace("-------NO ORDER YET ---------");
    else
        local _state = st[_orderId];
        local _rate = r[_orderId];
        local _rateStop = rs[_orderId];
        core.host:trace("===== OrderID: " .. _orderId);
        core.host:trace("======== State: " .. _state);
        core.host:trace("======== Entry/Stop Rate: " .. _rate .. "/" .. _rateStop);

    end
end


function getLastRecordedOrderId()
    if countEntry > 0 then
        return idtbl[countEntry - 1];
    else
        return nil;
    end
end



---Round a number using its instrument precision number
---@param oriNumber number
---@return number?
function toInstrumentPrecisionNumber(oriNumber)
    return tonumber(string.format("%." .. a.Precision .. "f", oriNumber));
end


---Round a number 0.00
---@param oriNumber number
---@return number?
function toMoneyNumber(oriNumber)
    return tonumber(string.format("%.2f", oriNumber));
end

function toShorten(oriNum, _decimal)
    return tonumber(string.format("%." .. _decimal .. "f", oriNum));
end





function checkReady(table)
    local rc;
    if a.Account == "TESTACC_ID" or a.Account == "TESTACCT" then
        -- run under debugger/simulator
        rc = true;
    else
        rc = core.host:execute("isTableFilled", table);
    end

    return rc;
end



dofile(core.app_path() .. "/strategies/standard/include/helper.lua");







Code: Select all
login=
password=
url=fxcorporate.com
connection=Demo

Account=

QM_HISTORY_PATH=.



SetLimit=true
Limit=70

strategy=SAVED_STRATEGY_NAME_HERE
instrument=USD/CAD
PingEachUpdate=false
TF=m1

MaxEntryLossRetry=2
LipsToleranceFilterP=1
TeethToleranceFilterP=2
ALLOWEDSIDE=Sell

BOP=1.3577
BOPFLAG=false

EntryExitTick=2

AllowTrade=true

UseUTC=false
jgwill
 
Posts: 23
Joined: Tue Aug 30, 2011 9:58 pm
Location: Maliotenam, Canada

Re: Backtesting and Real use behavioural contrast

Postby Apprentice » Sat Dec 24, 2022 8:31 am

Jgwill Strategy.lua
(39.36 KiB) Downloaded 184 times

Suggestions

1. Add exit logic
2. Use a smaller Limit (in pips)
if I reduce the pips to 30, some trades are closed
3. It seems that TrailingStop is NOT implemented


tested in simulation mode
User avatar
Apprentice
FXCodeBase: Confirmed User
 
Posts: 37884
Joined: Thu Dec 31, 2009 11:59 am
Location: Zagreb, Croatia


Return to Development

Who is online

Users browsing this forum: No registered users and 1 guest