Creating and modifying ELS/OCO orders in a FIFO account

Section for discussions related to indicators, use of indicators, and building of trading stategies using indicators.

Moderator: admin

Creating and modifying ELS/OCO orders in a FIFO account

Postby ascent » Wed Mar 28, 2012 11:44 am

Hi,

I've been reading the Indicore docs, and searching here. I found one topic here on ELS orders, but it seems a little incomplete.

Here's what I'm trying to accomplish in a FIFO account:

1. Place an order to buy or sell at market, simultaneously with a stop and limit order attached that is OCO, such that execution of the stop or limit cancels the other.

2. Over the course of the trade, modify the stop order, and possibly the limit order using code.

3. Also, if the initial order is closed for whatever reason not related to the stop or limit, the limit and stop orders are also canceled.

Ideally, these trades would be structured properly such that there is never, even for a fraction of a second, a risk of not being protected by the stop order. I'm assuming, or hoping this is possible by attaching the trades together before execution.

If I could see clear example code in the form of three separate functions, where each function takes care of one of the 3 cases above, that would be great.

Any help would be greatly appreciated. Thank you.
ascent
 
Posts: 7
Joined: Sat Jul 30, 2011 8:24 pm

Re: Creating and modifying ELS/OCO orders in a FIFO account

Postby sunshine » Fri Mar 30, 2012 3:18 am

ascent wrote:1. Place an order to buy or sell at market, simultaneously with a stop and limit order attached that is OCO, such that execution of the stop or limit cancels the other.

I think, OCO is not the best solution for risk management on US (FIFO) accounts. I'd recommend you to use ELS (Entry Limit Stop).

To create ELS order, use exactly the same way as for regular Stop/Limit orders (i.e. just attach Stops and Limits to Entry or Market order), but specify the EntryLimitStop flag.

Please see the example on how to open a position with Stop/Limit (ELS) below. This is a snippet from the standrad MA_Advisor.lua strategy.

Code: Select all
function Update()
    if not(init) then
        CANCLOSE = core.host:execute("getTradingProperty", "canCreateMarketClose", TICKSRC:instrument(), ACCOUNT);
        init = true;
        ...
    end
...
end

-- the method enters to the market
function enter(side)
    local valuemap;

    valuemap = core.valuemap();
    valuemap.OrderType = "OM";
    valuemap.OfferID = OFFER;
    valuemap.AcctID = ACCOUNT;
    valuemap.Quantity = AMOUNT;
    valuemap.CustomID = CID;
    valuemap.BuySell = side;
    if STOP >= 1 then
        valuemap.PegTypeStop = "O";
        if side == "B" then
            valuemap.PegPriceOffsetPipsStop = -STOP;
        else
            valuemap.PegPriceOffsetPipsStop = STOP;
        end
        if USE_TRAILING_STOP then
            valuemap.TrailStepStop = 1;
        end
    end
    if LIMIT >= 1 then
        valuemap.PegTypeLimit = "O";
        if side == "B" then
            valuemap.PegPriceOffsetPipsLimit = LIMIT;
        else
            valuemap.PegPriceOffsetPipsLimit = -LIMIT;
        end
    end

    if (not CANCLOSE) and (STOP >= 1 or LIMIT >= 1) then
        valuemap.EntryLimitStop = 'Y'
    end

    success, msg = terminal:execute(200, valuemap);
    assert(success, msg);
end


ascent wrote:2. Over the course of the trade, modify the stop order, and possibly the limit order using code.

Please see the function below. To change Stop (from ELS group), specify "SE" in the OrderType parameter. To change Limit order, specify "LE".
Note that the it is supposed that you have only one Stop and Limit per instrument. If you have a few Stops/Limits, you should define which of them should be modified (need to introduce additional parameter in the function, e.g. CurrentOrderPrice).
Code: Select all
function ChangeStopLimit(OrderType, Instrument, OrderPrice)
    enum = core.host:findTable("orders"):enumerator();
    while true do
        row = enum:next();
        if row == nil then
            break;
        end
        if row.Type == OrderType and -- OrderType must be equal to SE or LE.
           row.Instrument == Instrument and
           row.ContingencyType == 3 then
            -- order found
           local valuemap;
           valuemap = core.valuemap();
           valuemap.Command = "EditOrder";
           valuemap.OrderID = row.OrderID;
           valuemap.AcctID= row.AccountID;
           valuemap.Rate = OrderPrice;
           success, msg = terminal:execute(200, valuemap);
           if not(success) then
               terminal:alertMessage(instance.bid:instrument(), instance.bid[NOW], "Failed create limit " .. msg, instance.bid:date(NOW));
           end
        end
    end
end


ascent wrote:3. Also, if the initial order is closed for whatever reason not related to the stop or limit, the limit and stop orders are also canceled.

ELS order (Stop/Limit on US account) will be automatically removed by the system in case the positions have been closed (that is, when there are no any positions on the instrument).
sunshine
 

Re: Creating and modifying ELS/OCO orders in a FIFO account

Postby cwn6161 » Sat Mar 01, 2014 2:47 pm

Hello, I took this code snippet and tried to modify it sightly to fit my needs. The problem I am seeing is that after a few trades, it seems like the stop/limit orders are no longer updated.

This code basically enters on one bar, and closes itself on the following bar. Rinse and repeat. However I can only get a few trades to run before the cycle stops. What am I missing that's preventing the trades from closing properly?

Code: Select all
function Init()
    strategy:name("MVA Trend strategy");
    strategy:description("MVA Trend strategy, Enter as long as price closes above its halfway mark.");

    strategy.parameters:addGroup("MVA indicator parameters");
    strategy.parameters:addInteger("MVA_N", "Period of MVA", "Period of MVA", 9, 2, 1000);


    strategy.parameters:addGroup("Price Parameters");
    strategy.parameters:addString("TF", "Time Frame", "", "W1");
    strategy.parameters:setFlag("TF", core.FLAG_PERIODS);

    strategy.parameters:addGroup("Trading Parameters");
    strategy.parameters:addString("AccountID", "Account to trade on", "", "");
    strategy.parameters:setFlag("AccountID", core.FLAG_ACCOUNT);
    strategy.parameters:addInteger("AmountID", "Trade Amount in Lots", "", 10, 1, 100);

end

local MVA;

local Source;
local AccountID;
local AmountID;
local OfferID;
local LotSize;
local CustomID;

local buy = false;
local anOrderID;
local mTradeId = nil;

local rate = nil;
local orderid = nil;
local stopid = nil;
local limitid = nil;
local requestid = nil;
local row = nil;


function Prepare()

    assert(instance.parameters.TF ~= "t1", "The time frame must not be tick");

    local name;
    name = profile:id() .. "(" .. instance.bid:name() .. "." .. instance.parameters.TF .. "," ..
           "MVA(" .. instance.parameters.MVA_N .. "))";

    instance:name(name);

    AccountID = instance.parameters.AccountID;
    AmountID = instance.parameters.AmountID;
    LotSize = core.host:execute("getTradingProperty", "baseUnitSize", instance.bid:instrument(), AccountID);
    OfferID = core.host:findTable("offers"):find("Instrument", instance.bid:instrument()).OfferID;

    Source = ExtSubscribe(2, nil, instance.parameters.TF, true, "bar");
    MVA = core.indicators:create("MVA", Source, instance.parameters.MVA_N);

end

function ExtUpdate(id, source, period)  -- The method called every time when a new bid or ask price appears.
    local buyEntrance = 0;
   local stop = 0;
   
    MVA:update(core.UpdateLast);
   
    -- Check that we have enough data
    if (MVA.DATA:first() > (period - 1)) then
        return
    end
   
    local trades = core.host:findTable("trades");
    local haveTrades = (trades:find('AccountID', AccountID) ~= nil);
    local midpoint = 0;
   
    midpoint = (Source.high[period] + Source.low[period]) / 2;

    if (haveTrades) then

        --Trades exist, so look to close it, then enter the opposite direction
        if buy == true then
            --Update stop if the trade will continue
         if Source.close[period] == Source.close[period] then
            ChangeStopLimit("SE", Source.close[period]); -- should happen each time now
            end
        end
               
    else
      buy = false;
       
        --No current trades exist, so look to make one (for longs)
        if MVA.DATA[period] < Source.close[period]  and   
           Source.close[period] > midpoint           then
   
            --set up a buy trade
            buyEntrance = Source.close[period];
            stop = Source.low[period];
            Open("B", stop, buyEntrance);
            buy = true;
        end
    end
end

function ChangeStopLimit(OrderType, OrderPrice)
    enum = core.host:findTable("orders"):enumerator();
    while true do
        row = enum:next();
        if row == nil then
            break;
        end
        if row.Type == OrderType and -- OrderType must be equal to SE or LE.
           row.ContingencyType == 3 then
            -- order found
           local valuemap;
           valuemap = core.valuemap();
           valuemap.Command = "EditOrder";
           valuemap.OrderID = row.OrderID;
           valuemap.AcctID= row.AccountID;
           valuemap.Rate = OrderPrice;
         core.host:trace("trying to change stop or limit ");
           success, msg = terminal:execute(200, valuemap);
           if not(success) then
               terminal:alertMessage(instance.bid:instrument(), instance.bid[NOW], "Failed create limit " .. msg, instance.bid:date(NOW));
           end
        end
    end
end

-- The method enters to the market
function Open(side, stopValue, limitValue)
    local valuemap;

    valuemap = core.valuemap();
    valuemap.Command = "CreateOrder";

   valuemap.EntryLimitStop = 'Y';
    valuemap.OrderType = "OM";

    valuemap.OfferID = OfferID;
    valuemap.AcctID = AccountID;
    valuemap.Quantity = AmountID * LotSize;
    valuemap.CustomID = "MYID";

    valuemap.BuySell = side;
    valuemap.GTC = "GTC";
    valuemap.RateStop = stopValue;
   
    if side == "B" then   
        valuemap.Rate = limitValue; 
        valuemap.RateLimit = limitValue * 2;       
    end


    success, msg = terminal:execute(100, valuemap);

    assert(success, msg);
end

dofile(core.app_path() .. "\\strategies\\standard\\include\\helper.lua");
cwn6161
 
Posts: 5
Joined: Tue Jan 24, 2012 1:22 pm

Re: Creating and modifying ELS/OCO orders in a FIFO account

Postby moomoofx » Mon Mar 03, 2014 8:59 am

sunshine wrote:
ascent wrote:ELS order (Stop/Limit on US account) will be automatically removed by the system in case the positions have been closed (that is, when there are no any positions on the instrument).


Is that true? I don't have a FIFO account so I'm not very familiar with this. I'm under the impression a stop or limit on a FIFO account is just an entry order in the opposite direction and there is no way to link it to the position created from the original order.

cwn6161: Nice attempt with the code. There's a couple of things wrong though
- You should be passing a tick source to the creation of the MVA indicator, not a bar source.
- 'if Source.close[period] == Source.close[period] then' does exactly nothing as that condition will always be true.
- I think the whole use of the 'buy' flag is flawed and probably not necessary given you already have the check to see if you have trades.
- Your main problem is you are trying to set the stop value to the current price. That's not valid. If you want to do that, you should be closing the position not editing a stop order. If you modify your code to set the stop to the close value minus 1 pip, it works. It's probably only been working "some of the times" because lucky for you, the price has moved prior to your stop order getting through - which is I guess is like slippage but in a good way in your case ;-)

Cheers,
MooMooFX
User avatar
moomoofx
FXCodeBase: Confirmed User
 
Posts: 193
Joined: Wed Oct 23, 2013 10:26 pm
Location: Okinawa, Japan. http://moomooforex.com

Re: Creating and modifying ELS/OCO orders in a FIFO account

Postby cwn6161 » Sun Apr 06, 2014 11:15 pm

MooMooFX - Thanks for the help! Changing that stop value from the current price sure did the trick. The other things you pointed out are pieces of a bigger method that I didn't want to include to avoid any confusion, and I mistakenly forgot to remove those snippets.

I am a bit new to the code language, but how do I pass tick source to the MVA indicator? I want to use weekly candles to produce the MVA - how does that tie into the creation of the indicator? Thanks for the help again.
cwn6161
 
Posts: 5
Joined: Tue Jan 24, 2012 1:22 pm

Re: Creating and modifying ELS/OCO orders in a FIFO account

Postby moomoofx » Mon Apr 07, 2014 8:09 pm

The SDK documentation provides help on specific functions, like core.indicators:create

http://www.fxcodebase.com/documents/Ind ... reate.html

There's heaps of helpful articles on the Recommended Reading topic under Indicator Development. This sounds like what you really need to get through.

http://www.fxcodebase.com/code/viewtopi ... =28&t=2026

Enjoy.
User avatar
moomoofx
FXCodeBase: Confirmed User
 
Posts: 193
Joined: Wed Oct 23, 2013 10:26 pm
Location: Okinawa, Japan. http://moomooforex.com

Re: Creating and modifying ELS/OCO orders in a FIFO account

Postby fwcolbert » Thu Nov 19, 2015 4:00 pm

Hello I was seeing if there is a correction for this function to work? We are trying to use a ELS stop as a trigger to open a position in the opposite direction from a "close" and apply it to this strategy for FIFO. Any feed back is greatly appreciated. :D

Code: Select all
    --+------------------------------------------------------------------+
    --|                         Double Single Double Double Strategy.lua |
    --|                              Copyright © 2014, Gehtsoft USA LLC |
    --|                                            http://fxcodebase.com |
    --|                                        Developed by: Mario Jemic |                   
    --|                                            mario.jemic@gmail.com |
    --+------------------------------------------------------------------+

    function Init() --The strategy profile initialization
        strategy:name("Double Single  Double Double Strategy");
        strategy:description("");
       
       strategy.parameters:addGroup("Price");
        strategy.parameters:addString("Type", "Price Type", "", "Bid");
        strategy.parameters:addStringAlternative("Type", "Bid", "", "Bid");
        strategy.parameters:addStringAlternative("Type", "Ask", "", "Ask");
       
       strategy.parameters:addString("TF", "Time frame", "", "H1");
        strategy.parameters:setFlag("TF", core.FLAG_PERIODS);

       
       

        CreateTradingParameters();
    end

    function CreateTradingParameters()
        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("ExecutionType", "End of Turn / Live", "", "End of Turn");
        strategy.parameters:addStringAlternative("ExecutionType", "End of Turn", "", "End of Turn");
       strategy.parameters:addStringAlternative("ExecutionType", "Live", "", "Live");
       
       strategy.parameters:addBoolean("CloseOnOpposite", "Close On Opposite", "", true);
        strategy.parameters:addString("CustomID", "Custom Identifier", "The identifier that can be used to distinguish strategy instances", "123");
       
       strategy.parameters:addInteger("MaxNumberOfPositionInAnyDirection", "Max Number Of Open Position In Any Direction", "", 2, 1, 100);
       strategy.parameters:addInteger("MaxNumberOfPosition", "Max Number Of Position In One Direction", "", 1, 1, 100);
           
        strategy.parameters:addString("ALLOWEDSIDE", "Allowed side", "Allowed side for trading or signaling, can be Sell, Buy or Both", "Both");
        strategy.parameters:addStringAlternative("ALLOWEDSIDE", "Both", "", "Both");
        strategy.parameters:addStringAlternative("ALLOWEDSIDE", "Buy", "", "Buy");
        strategy.parameters:addStringAlternative("ALLOWEDSIDE", "Sell", "", "Sell");

        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("Alerts");
        strategy.parameters:addBoolean("ShowAlert", "ShowAlert", "", true);
        strategy.parameters:addBoolean("PlaySound", "Play Sound", "", false);
        strategy.parameters:addFile("SoundFile", "Sound File", "", "");
        strategy.parameters:setFlag("SoundFile", core.FLAG_SOUND);
        strategy.parameters:addBoolean("RecurrentSound", "Recurrent Sound", "", true);
        strategy.parameters:addBoolean("SendEmail", "Send Email", "", false);
        strategy.parameters:addString("Email", "Email", "", "");
        strategy.parameters:setFlag("Email", core.FLAG_EMAIL);
       
        strategy.parameters:addGroup("Time Parameters");
        strategy.parameters:addString("StartTime", "Start Time for Trading", "", "00:00:00");
        strategy.parameters:addString("StopTime", "Stop Time for Trading", "", "24:00:00");

        strategy.parameters:addBoolean("UseMandatoryClosing", "Use Mandatory Closing", "", false);
        strategy.parameters:addString("ExitTime", "Mandatory Closing  Time", "", "23:59:00");
        strategy.parameters:addInteger("ValidInterval", "Valid interval for operation in second", "", 60);
       
       
    end
    local OpenTime, CloseTime, ExitTime,ValidInterval;
    local Source,TickSource;
    local MaxNumberOfPositionInAnyDirection, MaxNumberOfPosition;
    local SoundFile = nil;
    local RecurrentSound = false;
    local ALLOWEDSIDE;
    local AllowTrade;
    local Offer;
    local CanClose;
    local Account;
    local Amount;
    local SetLimit;
    local Limit;
    local SetStop;
    local Stop;
    local TrailingStop;
    local ShowAlert;
    local Email;
    local SendEmail;
    local BaseSize;
    local ExecutionType;
    local CloseOnOpposite
    local first;
     
    local CustomID;

    -- Don't need to store hour + minute + second for each time
    local OpenTime, CloseTime, ExitTime;
    --
    function Prepare( nameOnly)
        CustomID = instance.parameters.CustomID;
       ExecutionType = instance.parameters.ExecutionType;
        CloseOnOpposite = instance.parameters.CloseOnOpposite;
       MaxNumberOfPositionInAnyDirection = instance.parameters.MaxNumberOfPositionInAnyDirection;
       MaxNumberOfPosition = instance.parameters.MaxNumberOfPosition;
       
       ValidInterval = instance.parameters.ValidInterval;
       UseMandatoryClosing = instance.parameters.UseMandatoryClosing;
       
       

        assert(instance.parameters.TF ~= "t1", "The time frame must not be tick");

        local name;
        name = profile:id() .. "( " .. instance.bid:name() .. "," .. CustomID ..  " )";
        instance:name(name);
       
        PrepareTrading();

        if nameOnly then
            return ;
        end
       
       if ExecutionType== "Live" then
       TickSource = ExtSubscribe(1, nil, "t1", instance.parameters.Type == "Bid", "close");
       end
       
        Source = ExtSubscribe(2, nil, instance.parameters.TF, instance.parameters.Type == "Bid", "bar");
        LongEMA = core.indicators:create("EMA", Source.close, instance.parameters.LongPeriods);
        ShortEMA = core.indicators:create("EMA", Source.close, instance.parameters.ShortPeriods);
        first = math.max(ShortEMA.DATA:first(), LongEMA.DATA:first()) + 1;
       
        local valid;
        OpenTime, valid = ParseTime(instance.parameters.StartTime);
        assert(valid, "Time " .. instance.parameters.StartTime .. " is invalid");
        CloseTime, valid = ParseTime(instance.parameters.StopTime);
        assert(valid, "Time " .. instance.parameters.StopTime .. " is invalid");
        ExitTime, valid = ParseTime(instance.parameters.ExitTime);
        assert(valid, "Time " .. instance.parameters.ExitTime .. " is invalid");
       
       if UseMandatoryClosing then
            core.host:execute("setTimer", 100, math.max(ValidInterval / 2, 1));
        end
       
       
    end


    -- NG: create a function to parse time
    function ParseTime(time)
        local Pos = string.find(time, ":");
        local h = tonumber(string.sub(time, 1, Pos - 1));
        time = string.sub(time, Pos + 1);
        Pos = string.find(time, ":");
        local m = tonumber(string.sub(time, 1, Pos - 1));
        local s = tonumber(string.sub(time, Pos + 1));
        return (h / 24.0 +  m / 1440.0 + s / 86400.0),                          -- time in ole format
               ((h >= 0 and h < 24 and m >= 0 and m < 60 and s >= 0 and s < 60) or (h == 24 and m == 0 and s == 0)); -- validity flag
    end

    function PrepareTrading()
        ALLOWEDSIDE = instance.parameters.ALLOWEDSIDE;

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

        ShowAlert = instance.parameters.ShowAlert;
        RecurrentSound = instance.parameters.RecurrentSound;

        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;
        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;
        SetStop = instance.parameters.SetStop;
        Stop = instance.parameters.Stop;
        TrailingStop = instance.parameters.TrailingStop;
    end
     
    local Last;
    local LAST;
    local ONE;


    function ExtUpdate(id, source, period)  -- The method called every time when a new bid or ask price appears.
        if AllowTrade then
            if not(checkReady("trades")) or not(checkReady("orders")) then
                return ;
            end
        end
       
       
       if  ExecutionType ==  "Live" and  id == 1 then
             
             period= core.findDate (Source.close, TickSource:date(period), false );     
                   
       end

       if  ExecutionType ==  "Live"  then
       
               if ONE == Source:serial(period) then
             return;
             end
       
             if id == 2 then
             return;
             end     
             
             
         
       else   
             if id ~= 2 then       
             return;
             end
       end

     
     

       if period < first  then
            return;
        end
       
         
        if    (Source.close[period-2]<Source.open[period-2]
       and Source.close[period-1]<Source.open[period-1]
        and Source.close[period]>Source.open[period]     
        and Source.close[period]> math.max(Source.close[period-1] ,Source.open[period-1] ) )   
       or
       (Source.close[period-3]<Source.open[period-3]
       and Source.close[period-2]<Source.open[period-2]
        and Source.close[period]>Source.open[period]   
        and Source.close[period]> math.max(Source.close[period-2] ,Source.open[period-2] ) )   
       
       then
            BUY();
          ONE= Source:serial(period);
        elseif    (Source.close[period-2]>Source.open[period-2]
       and Source.close[period-1]>Source.open[period-1]
       and Source.close[period]<Source.open[period]
       and Source.close[period]< math.min(Source.close[period-1] ,Source.open[period-1] )    )
       or
          (Source.close[period-3]>Source.open[period-3]
       and Source.close[period-2]>Source.open[period-2]
       and Source.close[period]<Source.open[period]
       and Source.close[period]< math.min(Source.close[period-2] ,Source.open[period-2] )    )
        then   
            SELL();
          ONE= Source:serial(period);
        end

    end

    -- NG: Introduce async function for timer/monitoring for the order results
    function ExtAsyncOperationFinished(cookie, success, message)

        if cookie == 100 then
            -- timer
            if UseMandatoryClosing and AllowTrade then
                now = core.host:execute("getServerTime");
                -- get only time
                now = now - math.floor(now);
                -- check whether the time is in the exit time period
                if now >= ExitTime and now < ExitTime + ValidInterval then
                    if not(checkReady("trades")) or not(checkReady("orders")) then
                        return ;
                    end
                    if haveTrades("S") then
                         exitSpecific("S");
                         Signal ("Close Short");
                    end
                    if haveTrades("B") then
                         exitSpecific("B");
                         Signal ("Close Long");
                   end
                end
            end
        elseif cookie == 200 and not success then
            terminal:alertMessage(instance.bid:instrument(), instance.bid[instance.bid:size() - 1], "Open order failed" .. message, instance.bid:date(instance.bid:size() - 1));
        elseif cookie == 201 and not success then
            terminal:alertMessage(instance.bid:instrument(), instance.bid[instance.bid:size() - 1], "Close order failed" .. message, instance.bid:date(instance.bid:size() - 1));
        end
    end

    --===========================================================================--
    --                    TRADING UTILITY FUNCTIONS                              --
    --============================================================================--
    function BUY()
        if AllowTrade then
            if CloseOnOpposite and haveTrades("S") then
                -- close on opposite signal
                exitSpecific("S");
                Signal ("Close Short");
            end

            if ALLOWEDSIDE == "Sell"  then
                -- we are not allowed buys.
                return;
            end

            enter("B");
        else
            Signal ("Buy Signal");   
        end
    end   
       
    function SELL ()     
        if AllowTrade then
            if CloseOnOpposite and haveTrades("B") then
                -- close on opposite signal
                exitSpecific("B");
                Signal ("Close Long");
            end

            if ALLOWEDSIDE == "Buy"  then
                -- we are not allowed sells.
                return;
            end

            enter("S");
        else
            Signal ("Sell Signal");   
        end
    end

    function Signal (Label)
        if ShowAlert then
            terminal:alertMessage(instance.bid:instrument(), instance.bid[NOW],  Label, instance.bid:date(NOW));
        end

        if SoundFile ~= nil then
            terminal:alertSound(SoundFile, RecurrentSound);
        end

        if Email ~= nil then
            terminal:alertEmail(Email, Label, profile:id() .. "(" .. instance.bid:instrument() .. ")" .. instance.bid[NOW]..", " .. Label..", " .. instance.bid:date(NOW));
        end
    end                       

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

        return rc;
    end

    function tradesCount(BuySell)
        local enum, row;
        local count = 0;
        enum = core.host:findTable("trades"):enumerator();
        row = enum:next();
        while row ~= nil do
            if row.AccountID == Account and row.OfferID == Offer and row.QTXT == CustomID and (row.BS == BuySell or BuySell == nil) then
                count = count + 1;
            end

            row = enum:next();
        end

        return count;
    end

    function haveTrades(BuySell)
        local enum, row;
        local found = false;
        enum = core.host:findTable("trades"):enumerator();
        row = enum:next();
        while (row ~= nil) do
            if row.AccountID == Account and row.OfferID == Offer and row.QTXT == CustomID and (row.BS == BuySell or BuySell == nil) then
                found = true;
                break;
            end

            row = enum:next();
        end

        return found;
    end

    -- enter into the specified direction
    function enter(BuySell)
        -- do not enter if position in the specified direction already exists
        if tradesCount(BuySell) >= MaxNumberOfPosition
       or ((tradesCount(nil)) >= MaxNumberOfPositionInAnyDirection)   
       then
            return true;
        end

        -- send the alert after the checks to see if we can trade.
        if (BuySell == "S") then
            Signal ("Sell Signal");   
        else
            Signal ("Buy Signal");   
        end

        return MarketOrder(BuySell);
    end


    -- enter into the specified direction
    function MarketOrder(BuySell)
        local valuemap, success, msg;
        valuemap = core.valuemap();

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

        -- FIFO:add stop/limit using Entry Limit Stop Orders.
        valuemap.EntryLimitStop = "Y";
          valuemap.PegTypeStop = "O";
           if SetStop then
               if BuySell == "B" then
                   valuemap.PegPriceOffsetPipsStop = -Stop;
               else
                   valuemap.PegPriceOffsetPipsStop = Stop;
               end
           end
           if TrailingStop then
               valuemap.TrailStepStop = 1;
           end

           valuemap.PegTypeLimit = "O";
           if SetLimit then
               if BuySell == "B" then
                   valuemap.PegPriceOffsetPipsLimit = Limit;
               else
                   valuemap.PegPriceOffsetPipsLimit = -Limit;
               end
            end
       
       if CloseOrderPrice or StopOrderPrice == "ClosePrice" then
           valuemap.EntryLimitStop = "Y";
             valuemap.PegTypeStop = "O";
              if SetStop then
                  if BuySell == "B" then
                      valuemap.PegPriceOffsetPipsStop = -Stop;
                  else
                      valuemap.PegPriceOffsetPipsStop = Stop;
                  end
              end
              if TrailingStop then
                  valuemap.TrailStepStop = 1;
              end

              valuemap.PegTypeLimit = "O";
              if SetLimit then
                  if BuySell == "B" then
                      valuemap.PegPriceOffsetPipsLimit = Limit;
                  else
                      valuemap.PegPriceOffsetPipsLimit = -Limit;
                  end
              end
       
            success, msg = terminal:execute(200, valuemap);

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

        if (not CanClose) then
            valuemap.EntryLimitStop = "Y"
        end

        success, msg = terminal:execute(200, valuemap);

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

    -- exit from the specified trade using the direction as a key
    function exitSpecific(BuySell)
        -- we have to loop through to exit all trades in each direction instead
        -- of using the net qty flag because we may be running multiple strategies on the same account.
        local enum, row;
        local found = false;
        enum = core.host:findTable("trades"):enumerator();
        row = enum:next();
        while (not found) and (row ~= nil) do
            -- for every trade for this instance.
            if row.AccountID == Account and row.OfferID == Offer and row.QTXT == CustomID and (row.BS == BuySell or BuySell == nil) then
               exitTrade(row);
            end

            row = enum:next();
        end
    end

    -- exit from the specified direction
    function exitTrade(tradeRow)
        if not(AllowTrade) then
            return true;
        end

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

        -- switch the direction since the order must be in oppsite direction
        if tradeRow.BS == "B" then
            BuySell = "S";
        else
            BuySell = "B";
        end
        valuemap.OrderType = "CM";
        valuemap.OfferID = Offer;
        valuemap.AcctID = Account;
        if (CanClose) then
            -- Non-FIFO can close each trade independantly.
            valuemap.TradeID = tradeRow.TradeID;
            valuemap.Quantity = tradeRow.Lot;
        else
            -- FIFO.
            valuemap.NetQtyFlag = "Y"; -- this forces all trades to close in the opposite direction.
        end
        valuemap.BuySell = BuySell;
        valuemap.CustomID = CustomID;
        success, msg = terminal:execute(201, valuemap);

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

        return true;
    end

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











fwcolbert
 
Posts: 8
Joined: Sat Jun 27, 2015 3:54 pm

Re: Creating and modifying ELS/OCO orders in a FIFO account

Postby Julia CJ » Fri Nov 27, 2015 6:36 am

Hi Fwcolbert,

You can post a request on the forum in this thread, this is for free.
However, your strategy will be shared with other users.
Or you can post your request in this thread , this is not a free service (please read more here).).
in this case your strategy will be developed privately.
Julia CJ
 


Return to Discussions

Who is online

Users browsing this forum: No registered users and 16 guests

cron