Running multiple instances of a strategy

Moderator: admin

Running multiple instances of a strategy

Postby dmancyu » Tue Nov 08, 2011 4:10 am

Is there anyone who had run multiple instances of a strategy? (i.e. running the same strategy on 2 or more currency pairs)

When running the same strategy for 2 or more currency pairs, it is likely the orders and trades tables would get locked out by one of the instance, causing update of other instances to fail. Is there anyway to resolve it?

Any insights / experience sharing would be much appreciated. ;)
dmancyu
 
Posts: 18
Joined: Thu Nov 03, 2011 10:50 pm

Re: Running multiple instances of a strategy

Postby sunshine » Tue Nov 08, 2011 4:53 am

dmancyu wrote:When running the same strategy for 2 or more currency pairs, it is likely the orders and trades tables would get locked out by one of the instance, causing update of other instances to fail.

There should not be such an issue.
Do you experience this problem? If so, could you please provide a code of your strategy.
sunshine
 

Re: Running multiple instances of a strategy

Postby dmancyu » Tue Nov 08, 2011 8:46 am

The code would run ok on one currency pair, but not two. Errors will popup when trying to update stop and limit. Give it a try...

Code: Select all
   
    function Init()
        -- User-friendly name and the description
        strategy:name("Test1");
        strategy:description("Test1");

        -- DNC parameters
        strategy.parameters:addGroup("DNC Parameters");
        strategy.parameters:addInteger("S", "Slow Trend", "", 20, 1, 200);

        -- Price subscription parameters (bid or ask price, time frame)
        strategy.parameters:addGroup("Price");
        strategy.parameters:addString("PT", "Price Type", "", "Bid");
        strategy.parameters:addStringAlternative("PT", "Bid", "", "Bid");
        strategy.parameters:addStringAlternative("PT", "Ask", "", "Ask");
        strategy.parameters:addString("TF", "Time Frame", "", "m1");
        strategy.parameters:setFlag("TF", core.FLAG_PERIODS);

        -- Alert parameters
        strategy.parameters:addGroup("Alerts");
        strategy.parameters:addBoolean("ShowAlert", "Show Alert", "", false);
        strategy.parameters:addBoolean("PlaySound", "Play Sound", "", false);
        strategy.parameters:addFile("SoundFile", "Sound File", "", "");
        strategy.parameters:setFlag("SoundFile", core.FLAG_SOUND);

        -- Trading parameters
        strategy.parameters:addGroup("Trading");
        strategy.parameters:addBoolean("CanTrade", "Allow Trading", "", true);
        strategy.parameters:addString("Account", "Account to trade", "", "");
        strategy.parameters:setFlag("Account", core.FLAG_ACCOUNT);
        strategy.parameters:addInteger("LotSize", "Size of the trade in lots", "", 1, 1, 100);
        strategy.parameters:addInteger("Entry", "Distance in pips for entry point", "", 3, 1, 100);
        strategy.parameters:addInteger("Stop", "Distance in pips for the stop order", "Use 0 to do not use stops", 5, 0, 100);
        strategy.parameters:addInteger("Limit", "Distance in pips for the limit order", "Use 0 to do not use limits", 5, 0, 100);
    end

    -- The global variables
    local price = nil;      -- the price history we subscribed for
    local slowMA;           -- slow DNC indicator
    local first;            -- the index of the oldest period where we can check whether moving averages has been crossed
    local CanTrade;         -- flag indicating whether we can trade
    local Amount;           -- the amount for the trade
    local Account;          -- the account to trade on
    local Stop;             -- the stop order level expressed in pips or 0 if no stop must be used
    local Limit;            -- the limit order level expressed in pips or 0 if no stop must be used
    local Entry;            -- entry distance expressed in pips
    local CanClose;         -- the flag indicating whether "close market" orders is allowed
    local CanStop;
    local OfferID;          -- the internal indentifier of the instrument (required for the orders)
    local SoundFile;        -- the sound file name or nil if no sound must be played
   
    --local LossCounter = 0;        -- Loss counter
   
     

    -- Prepare all the data.
    -- The function is called once when the strategy is about to be started.
    function Prepare()
        -- check moving average parameters
        local S;
        S = instance.parameters.S;

        -- check alerts settings
        local ShowAlert;
        ShowAlert = instance.parameters.ShowAlert;
        if instance.parameters.PlaySound then
            SoundFile = instance.parameters.SoundFile;
        else
            SoundFile = nil;
        end
        assert(not(PlaySound) or (PlaySound and SoundFile ~= ""), "Sound file must be specified");

        -- check whether the strategy is allowed to trade
        CanTrade = instance.parameters.CanTrade;
        if CanTrade then
            -- Prepare the common information (account, stop, limit and OfferID (internal id of the instrument).
            Account = instance.parameters.Account;
            Entry = math.floor(instance.parameters.Entry + 0.5);
            Stop = math.floor(instance.parameters.Stop + 0.5);
            Limit = math.floor(instance.parameters.Limit + 0.5);
            local instrument = instance.bid:instrument();
            OfferID = core.host:findTable("offers"):find("Instrument", instrument).OfferID;
            -- check whether stop and limit orders are allowed
            CanStop = core.host:execute("getTradingProperty", "canCreateStopLimit", instrument, Account);
            -- Check whether close market orders are allowed
            CanClose = core.host:execute("getTradingProperty", "canCreateMarketClose", instrument, Account);
            -- And finally turn "in lots" amount into the absolule value.
            Amount = instance.parameters.LotSize * core.host:execute("getTradingProperty", "baseUnitSize", instrument, Account);

        end
        -- name the indicator
        local name = profile:id() .. "(" .. instance.bid:name() .. "," .. instance.parameters.TF .. ", " .. S .. ")";
        instance:name(name);
        -- setup the signal. pay attention, we pass "ShowAlert" (value initially taken from the instance.parameters.ShowAlert)
        -- here, so, we don't check whether alerts are requested anymore.
        ExtSetupSignal(name, ShowAlert);
       
        -- and finally subscribe for the ticks of the instrument the user initially chosen to run the strategy for to
        -- have our strategy activated once.
        ExtSubscribe(1, nil, "t1", true, "tick");

        -- firstRun = true;

    end

    -- the function is called every time when any subscribed price is changed. For tick subscribtions the function is called
    -- for every tick, for the bar subscribtions the function is called when the candle is closed (in other words, when
    -- the first tick of the next candle appears).
    function ExtUpdate(id, source, period)
        if id == 1 and price == nil then
            -- our tick subscribtion. do it on the first tick if the
            -- user chosen subscribtion has not been not made yet.

            -- subscribe for the user chosen timeframe/to close prices
            price = ExtSubscribe(2, nil, instance.parameters.TF, true, "bar");
            slowMA = core.indicators:create("DNC", price, instance.parameters.S, "yes");
           
            -- and get the oldest index of the bar we can work at
            first = slowMA.DATA:first() + 1;
        elseif id == 2 then
            -- on the user chosen subscription (can be either tick or bar subscribtion).

            -- update indicators
            slowMA:update(core.UpdateLast);
           
            -- if we have enough bars in the history to work
            if period >= first then
                local slowDU = slowMA.DU[period];
                local slowDN = slowMA.DN[period];
               
                if not haveTrades() then

                  -- if there is no active trades, create entry orders
                  local valuemap = nil;
                  if not haveOrders() then

                    -- Create OCO order
                    valuemap = core.valuemap();
                    valuemap.Command = "CreateOCO";
                    valuemap:append(open(slowDN, false));  -- append long order
                    valuemap:append(open(slowDU, true));   -- append short order
               
                    local success, msg;
                    success, msg = terminal:execute(100, valuemap);
                    assert(success, msg);                               
                  else  -- edit order
                    local enum, row, entryPoint;
                    local success, msg;

                    enum = core.host:findTable("orders"):enumerator();
                    while true do
                        row = enum:next();
                        if row == nil then
                            break;
                        end
                       
                        -- update Entry Point                 
                        if row.Type == "SE" then
                            local valuemap = nil;
                            local entryPoint;
                            if row.BS == "B" then-- long
                                -- modify Rate first in value
                                valuemap = core.valuemap();
                                valuemap.Command = "EditOrder";
                                valuemap.OfferID = OfferID;
                                valuemap.AcctID = Account;
                                valuemap.OrderID = row.OrderID;
                                valuemap.Rate = slowDN + Entry * instance.ask:pipSize();
                                entryPoint = valuemap.Rate;
                                success, msg = terminal:execute(100, valuemap);
                                if not success then
                                    core.host:trace("Error detected when creating Long Entry Point... ");
                                    return;
                                end

                                -- then modify the stop value in pips
                                valuemap = core.valuemap();
                                valuemap.Command = "EditOrder";
                                valuemap.OfferID = OfferID;
                                valuemap.AcctID = Account;
                                valuemap.OrderID = row.StopOrderID;
                                valuemap.Rate = entryPoint - Stop * instance.ask:pipSize();
                                success, msg = terminal:execute(100, valuemap);
                                if not success then
                                    core.host:trace("Error detected when creating Long Stop... ");
                                    return;
                                end

                                -- modify the limit value in pips
                                valuemap = core.valuemap();
                                valuemap.Command = "EditOrder";
                                valuemap.OfferID = OfferID;
                                valuemap.AcctID = Account;
                                valuemap.OrderID = row.LimitOrderID;
                                valuemap.Rate = entryPoint + Limit * instance.bid:pipSize();
                                success, msg = terminal:execute(100, valuemap);
                                if not success then
                                    core.host:trace("Error detected when creating Long Limit... ");
                                    return;
                                end
                            else -- short
                                -- modify Rate first in value
                                local valuemap = core.valuemap();
                                valuemap.Command = "EditOrder";
                                valuemap.OfferID = OfferID;
                                valuemap.AcctID = Account;
                                valuemap.OrderID = row.OrderID;
                                valuemap.Rate = slowDU - Entry * instance.bid:pipSize();
                                entryPoint = valuemap.Rate
                                success, msg = terminal:execute(100, valuemap);
                                if not success then
                                    core.host:trace("Error detected when creating Short Entry Point... ");
                                    return;
                                end

                                -- then modify the stop value in pips
                                valuemap = core.valuemap();
                                valuemap.Command = "EditOrder";
                                valuemap.OfferID = OfferID;
                                valuemap.AcctID = Account;
                                valuemap.OrderID = row.StopOrderID;
                                valuemap.Rate = entryPoint + Stop * instance.ask:pipSize();
                                success, msg = terminal:execute(100, valuemap);
                                if not success then
                                    core.host:trace("Error detected when creating Short Stop... ");
                                    return;
                                end

                                -- modify the limit value in pips
                                valuemap = core.valuemap();
                                valuemap.Command = "EditOrder";
                                valuemap.OfferID = OfferID;
                                valuemap.AcctID = Account;
                                valuemap.OrderID = row.LimitOrderID;
                                valuemap.Rate = entryPoint - Limit * instance.bid:pipSize();
                                success, msg = terminal:execute(100, valuemap);
                                if not success then
                                    core.host:trace("Error detected when creating Short Limit...");
                                    return;
                                end
                            end
                        end

                      end
                  end
                else
                  -- Trading active, no need to do anything... just to keep it simple...
                end
            end
        end
    end

    -- The function opens the position in the chosen direction.
    -- Account, amount and instrument (aka offer) are predefined in
    -- the Prepare() function.
    function open(trendValue, sell)
        -- create and fill value map with the predefined order parameters
        local valuemap;
        valuemap = core.valuemap();
        valuemap.Command = "CreateOrder";       -- command: create new order
        valuemap.OfferID = OfferID;             -- instrument
        valuemap.AcctID = Account;
        valuemap.Quantity = Amount;

        -- fill the side and calculate stops and limits.
        -- the Stop and Limit global variables are expressed in pips.
        -- pay attention that we calculate prices against different prices
        -- to avoid setting the stop or limit inside the spread.
        -- Also, the stop and limit offset depends on the trade direction. For example
        -- the stop for a buy position is below the price while the stop for a sell position
        -- is above the price.
        local side, limit, stop;
        if sell then
            side = "S";
           
            -- if it is a short order, we would guess the price level is going down.
            valuemap.OrderType = "SE";
            valuemap.Rate = trendValue - Entry * instance.bid:pipSize();  -- just leave the constant here because don't know how to set constant
     
            -- set stop loss and limit according to the parameter
            -- both parameter cannot be set < 3
            if CanStop then
              if Limit > 0 then
                valuemap.PegTypeLimit = "O";
                valuemap.PegPriceOffsetPipsLimit = -Limit;       
              end
              if Stop > 0 then
                valuemap.PegTypeStop = "O";
                valuemap.PegPriceOffsetPipsStop = Stop;       
              end
            end
        else
            side = "B";
           
            -- if it is a long order, we would guess the price level is going up.
            valuemap.OrderType = "SE"; 
            valuemap.Rate = trendValue + Entry * instance.ask:pipSize();  -- just leave the constant here because don't know how to set constant
           
            -- set stop loss and limit according to the parameter
            -- both parameter cannot be set < 3
            if CanStop then           
              if Limit > 0 then
                valuemap.PegTypeLimit = "O";
                valuemap.PegPriceOffsetPipsLimit = Limit;       
              end
              if Stop > 0 then
                valuemap.PegTypeStop = "O";
                valuemap.PegPriceOffsetPipsStop = -Stop;       
              end             
            end                               
        end

        -- fill side and stop/limit orders in the value map
        valuemap.BuySell = side;

        return valuemap;
    end
   
    function DeleteOrders()
       local enum, row;
 
       enum = core.host:findTable("orders"):enumerator();
       row = enum:next();
       while (row ~= nil) do
           if row.FixStatus == "W" and
                    row.AccountID == Account and
                        row.OfferID == OfferID then
               local valuemap = core.valuemap();
               valuemap.Command = "DeleteOrder";
               valuemap.OrderID = row.OrderID;
              local success, msg = terminal:execute(200, valuemap);
              assert(success, msg);
           end
           row = enum:next();
       end
    end

    -- function which determines whether trade was being conducted
    function haveTrades()
      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 == OfferID then
           found = true;
           return found;
        end
        row = enum:next();
      end

      return found;
    end

    -- function which determines whether trade was being conducted
    function haveOrders()
      local enum, row;
      local found = false;
      enum = core.host:findTable("orders"):enumerator();
      row = enum:next();
      while (not found) and (row ~= nil) do
        if row.AccountID == Account and
           row.OfferID == OfferID then
           found = true;
           return found;
        end
        row = enum:next();
      end

      return found;
    end


    -- use the helpers
    dofile(core.app_path() .. "\\strategies\\standard\\include\\helper.lua");













dmancyu
 
Posts: 18
Joined: Thu Nov 03, 2011 10:50 pm

Re: Running multiple instances of a strategy

Postby dmancyu » Tue Nov 08, 2011 11:31 pm

I found the problem which I forgot to check the accountid and offerid before update. Thanks for your help. :)
dmancyu
 
Posts: 18
Joined: Thu Nov 03, 2011 10:50 pm

Re: Running multiple instances of a strategy

Postby dmancyu » Fri Dec 09, 2011 10:55 am

Kris,
You don't need to make things complicated... just write the strategy as it runs by itself, you don't need to worry about concurrency as TSII will handle it for you.

I am now running the same strategy on 2 or more pairs of currency without problem now... my tip to you is, make sure you check the AccountID and OfferID before you modify and close the trade. :)
dmancyu
 
Posts: 18
Joined: Thu Nov 03, 2011 10:50 pm


Return to Indicator Development

Who is online

Users browsing this forum: No registered users and 50 guests