Also, for anyone who is familiar with Forexmentor and their Slingshot trade strategy, I'm trying to modify this code to detect this setup. For the most part, it's the opposite of what this code was doing. Trigger a "B" when the Stochastic is overbought and flat, Trigger a "S" when it is oversold and flat. Then add the 8 EMA crosses the 20 SMA, and it should be a really super strategy with very tight stops.
Any help solving this Stop/Limit bug will be much appreciated.
Thank you.
- Code: Select all
function Init() --The strategy profile initialization
strategy:name("Stochastic+BB strategy");
strategy:description("MACD Divergence+BB strategy");
strategy.parameters:addGroup("Stochastic parameters");
strategy.parameters:addInteger("Stoch_K", "Number of periods for %K", "Number of periods for %K", 5, 2, 1000);
strategy.parameters:addInteger("Stoch_SD", "%D slowing periods", "%D slowing periods", 3, 2, 1000);
strategy.parameters:addInteger("Stoch_D", "Number of periods for %D", "Number of periods for %D", 3, 2, 1000);
strategy.parameters:addString("Stoch_MVAT_K", "Smoothing type for %K", "Smoothing type for %K", "MVA");
strategy.parameters:addStringAlternative("Stoch_MVAT_K", "MVA", "", "MVA");
strategy.parameters:addStringAlternative("Stoch_MVAT_K", "EMA", "", "EMA");
strategy.parameters:addStringAlternative("Stoch_MVAT_K", "FS", "", "FS");
strategy.parameters:addString("Stoch_MVAT_D", "Smoothing type for %D", "Smoothing type for %D", "MVA");
strategy.parameters:addStringAlternative("Stoch_MVAT_D", "MVA", "", "MVA");
strategy.parameters:addStringAlternative("Stoch_MVAT_D", "MVA", "", "EMA");
strategy.parameters:addGroup("Bands parameters");
strategy.parameters:addInteger("BB_N", "Number of periods", "Number of periods", 20, 1, 10000);
strategy.parameters:addDouble("BB_Dev", "Number of standard deviation", "Number of standard deviation", 2.0, 0.0001, 1000.0);
strategy.parameters:addString("BB_Price", "Price for bands", "", "close");
strategy.parameters:addStringAlternative("BB_Price", "close", "", "close");
strategy.parameters:addStringAlternative("BB_Price", "open", "", "open");
strategy.parameters:addStringAlternative("BB_Price", "high", "", "high");
strategy.parameters:addStringAlternative("BB_Price", "low", "", "low");
strategy.parameters:addStringAlternative("BB_Price", "median", "", "median");
strategy.parameters:addStringAlternative("BB_Price", "typical", "", "typical");
strategy.parameters:addStringAlternative("BB_Price", "weighted", "", "weighted");
strategy.parameters:addGroup("Strategy Parameters");
strategy.parameters:addDouble("OversoldLevel", "Oversold level", "Oversold level", 20, 0, 100);
strategy.parameters:addDouble("OverboughtLevel", "Overbought level", "Overbought level", 80, 0, 100);
strategy.parameters:addInteger("MaxDistance", "Max. distance between Stochastic and BB events", "Max. distance between Stochastic and BB events", 1, 0, 1000);
-- strategy.parameters:addString("TypeSignal", "Type of signal", "", "direct");
-- strategy.parameters:addStringAlternative("TypeSignal", "direct", "", "direct");
-- strategy.parameters:addStringAlternative("TypeSignal", "reverse", "", "reverse");
strategy.parameters:addGroup("Price Parameters");
strategy.parameters:addString("TF", "Time Frame", "", "m15");
strategy.parameters:setFlag("TF", core.FLAG_PERIODS);
strategy.parameters:addGroup("Trading Parameters");
strategy.parameters:addBoolean("AllowTrade", "Allow strategy to trade", "", false);
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:addString("AllowDirection", "Allow direction for positions", "", "Both");
strategy.parameters:addStringAlternative("AllowDirection", "Both", "", "Both");
strategy.parameters:addStringAlternative("AllowDirection", "Long", "", "Long");
strategy.parameters:addStringAlternative("AllowDirection", "Short", "", "Short");
strategy.parameters:addGroup("Signal Parameters");
strategy.parameters:addBoolean("ShowAlert", "Show Alert", "", true);
strategy.parameters:addBoolean("PlaySound", "Play Sound", "", false);
strategy.parameters:addFile("SoundFile", "Sound File", "", "");
strategy.parameters:setFlag("SoundFile", core.FLAG_SOUND);
strategy.parameters:addBoolean("Recurrent", "RecurrentSound", "", false);
strategy.parameters:addGroup("Email Parameters");
strategy.parameters:addBoolean("SendEmail", "Send email", "", false);
strategy.parameters:addString("Email", "Email address", "", "");
strategy.parameters:setFlag("Email", core.FLAG_EMAIL);
end
-- Signal Parameters
local ShowAlert;
local SoundFile;
local RecurrentSound;
local SendEmail, Email;
-- Internal indicators
local Stoch = nil;
local BB = nil;
-- Strategy parameters
local openLevel = 0
local closeLevel = 0
local confirmTrend;
-- Trading parameters
local AllowTrade = nil;
local Account = nil;
local Amount = nil;
local BaseSize = nil;
local PipSize;
local SetLimit = nil;
local Limit = nil;
local SetStop = nil;
local Stop = nil;
local TrailingStop = nil;
local CanClose = nil;
local AllowDirection;
--
--
--
function Prepare()
ShowAlert = instance.parameters.ShowAlert;
AllowDirection = instance.parameters.AllowDirection;
local PlaySound = instance.parameters.PlaySound
if PlaySound then
SoundFile = instance.parameters.SoundFile;
else
SoundFile = nil;
end
assert(not(PlaySound) or SoundFile ~= "", "Sound file must be chosen");
RecurrentSound = instance.parameters.Recurrent;
local SendEmail = instance.parameters.SendEmail;
if SendEmail then
Email = instance.parameters.Email;
else
Email = nil;
end
assert(not(SendEmail) or Email ~= "", "Email address must be specified");
assert(instance.parameters.TF ~= "t1", "The time frame must not be tick");
local name;
name = profile:id() .. "(" .. instance.bid:name() .. "." .. instance.parameters.TF;
instance:name(name);
AllowTrade = instance.parameters.AllowTrade;
if AllowTrade then
Account = instance.parameters.Account;
Amount = instance.parameters.Amount;
BaseSize = core.host:execute("getTradingProperty", "baseUnitSize", instance.bid:instrument(), Account);
Offer = core.host:findTable("offers"):find("Instrument", instance.bid:instrument()).OfferID;
CanClose = core.host:execute("getTradingProperty", "canCreateMarketClose", instance.bid:instrument(), Account);
PipSize = instance.bid:pipSize();
SetLimit = instance.parameters.SetLimit;
Limit = instance.parameters.Limit;
SetStop = instance.parameters.SetStop;
Stop = instance.parameters.Stop;
TrailingStop = instance.parameters.TrailingStop;
end
Source = ExtSubscribe(2, nil, instance.parameters.TF, true, "bar");
Stoch = core.indicators:create("STOCHASTIC", Source, instance.parameters.Stoch_K, instance.parameters.Stoch_SD, instance.parameters.Stoch_D, instance.parameters.Stoch_MVAT_K, instance.parameters.Stoch_MVAT_D);
Price=instance.parameters.BB_Price;
if Price=="close" then
BB = core.indicators:create("BB", Source.close, instance.parameters.BB_N, instance.parameters.BB_Dev);
elseif Price=="open" then
BB = core.indicators:create("BB", Source.open, instance.parameters.BB_N, instance.parameters.BB_Dev);
elseif Price=="high" then
BB = core.indicators:create("BB", Source.high, instance.parameters.BB_N, instance.parameters.BB_Dev);
elseif Price=="low" then
BB = core.indicators:create("BB", Source.low, instance.parameters.BB_N, instance.parameters.BB_Dev);
elseif Price=="typical" then
BB = core.indicators:create("BB", Source.typical, instance.parameters.BB_N, instance.parameters.BB_Dev);
elseif Price=="median" then
BB = core.indicators:create("BB", Source.median, instance.parameters.BB_N, instance.parameters.BB_Dev);
else
BB = core.indicators:create("BB", Source.weighted, instance.parameters.BB_N, instance.parameters.BB_Dev);
end
ExtSetupSignal(profile:id() .. ":", ShowAlert);
ExtSetupSignalMail(name);
end
function ExtUpdate(id, source, period) -- The method called every time when a new bid or ask price appears.
Stoch:update(core.UpdateLast);
BB:update(core.UpdateLast);
-- Check that we have enough data
if (Stoch.DATA:first() > (period - 1)) then
return
end
if (BB.DATA:first() > (period - 1)) then
return
end
local pipSize = instance.bid:pipSize()
local trades = core.host:findTable("trades");
local haveTrades = (trades:find('AccountID', Account) ~= nil)
local MustOpenB=false;
local MustOpenS=false;
if Stoch.K[period]<=instance.parameters.OversoldLevel and Stoch.D[period]<=instance.parameters.OversoldLevel then
if BB_Touch("U",period) then
MustOpenS=true;
end
end
if Stoch.K[period]>=instance.parameters.OverboughtLevel and Stoch.D[period]>=instance.parameters.OverboughtLevel then
if BB_Touch("L",period) then
MustOpenB=true;
end
end
if (haveTrades) then
local enum = trades:enumerator();
while true do
local row = enum:next();
if row == nil then break end
if row.AccountID == Account and row.OfferID == Offer then
-- Close position if we have corresponding closing conditions.
if row.BS == 'B' then
if MustOpenS then
if ShowAlert then
if instance.parameters.AllowDirection=="Long" then
ExtSignal(source, period, "Close BUY", SoundFile, Email, RecurrentSound);
else
ExtSignal(source, period, "Close BUY and SELL", SoundFile, Email, RecurrentSound);
end
end
if AllowTrade then
Close(row);
if instance.parameters.AllowDirection~="Long" then
Open("S")
end
end
end
elseif row.BS == 'S' then
if MustOpenB then
if ShowAlert then
if instance.parameters.AllowDirection=="Short" then
ExtSignal(source, period, "Close SELL", SoundFile, Email, RecurrentSound);
else
ExtSignal(source, period, "Close SELL and BUY", SoundFile, Email, RecurrentSound);
end
end
if AllowTrade then
Close(row);
if instance.parameters.AllowDirection~="Short" then
Open("B")
end
end
end
end
end
end
else
if MustOpenB==true and instance.parameters.AllowDirection~="Short" then
if ShowAlert then
ExtSignal(source, period, "BUY", SoundFile, Email, RecurrentSound)
end
if AllowTrade then
Open("B")
end
end
if MustOpenS==true and instance.parameters.AllowDirection~="Long" then
if ShowAlert then
ExtSignal(source, period, "SELL", SoundFile, Email, RecurrentSound)
end
if AllowTrade then
Open("S")
end
end
end
end
-- The strategy instance finalization.
function ReleaseInstance()
end
-- The method enters to the market
function Open(side)
local valuemap;
valuemap = core.valuemap();
valuemap.OrderType = "OM";
valuemap.OfferID = Offer;
valuemap.AcctID = Account;
valuemap.Quantity = Amount * BaseSize;
valuemap.CustomID = CID;
valuemap.BuySell = side;
valuemap.QTXT="1";
if SetStop and CanClose then
valuemap.PegTypeStop = "O";
if side == "B" then
valuemap.PegPriceOffsetPipsStop = -Stop;
else
valuemap.PegPriceOffsetPipsStop = Stop;
end
if TrailingStop then
valuemap.TrailStepStop = 1;
end;
end
if SetLimit and CanClose then
valuemap.PegTypeLimit = "O";
if side == "B" then
valuemap.PegPriceOffsetPipsLimit = Limit;
else
valuemap.PegPriceOffsetPipsLimit = -Limit;
end
end
success, msg = terminal:execute(200, valuemap);
assert(success, msg);
-- FIFO Account, in that case we have to open Net Limit and Stop Orders
if not(CanClose) then
if SetStop then
valuemap = core.valuemap();
valuemap.OrderType = "SE"
valuemap.OfferID = Offer;
valuemap.AcctID = Account;
valuemap.NetQtyFlag = 'y';
if side == "B" then
valuemap.BuySell = "S";
rate = instance.ask[NOW] - Stop * PipSize;
valuemap.Rate = rate;
elseif side == "S" then
valuemap.BuySell = "B";
rate = instance.bid[NOW] + Stop * PipSize;
valuemap.Rate = rate;
end
if TrailingStop then
valuemap.TrailUpdatePips = 1
end
success, msg = terminal:execute(200, valuemap);
--core.host:trace('Set stop @ ' .. rate);
assert(success, msg);
end
if SetLimit then
valuemap = core.valuemap();
valuemap.OrderType = "LE"
valuemap.OfferID = Offer;
valuemap.AcctID = Account;
valuemap.NetQtyFlag = 'y';
if side == "B" then
valuemap.BuySell = "S";
rate = instance.ask[NOW] + Limit * PipSize;
valuemap.Rate = rate;
elseif side == "S" then
valuemap.BuySell = "B";
rate = instance.bid[NOW] - Limit * PipSize;
valuemap.Rate = rate;
end
success, msg = terminal:execute(200, valuemap);
--core.host:trace('Set limit @ ' .. rate);
assert(success, msg);
end
end
end
function BB_Touch(Band,period)
local period_=period;
while period_>Source:first() and period-period_<=instance.parameters.MaxDistance do
if Band=="U" and Source.high[period_]>=BB.TL[period_] then
return true;
elseif Band=="L" and Source.low[period_]<=BB.BL[period_] then
return true;
end
period_=period_-1;
end
return false;
end
-- Closes specific position
function Close(trade)
local valuemap;
valuemap = core.valuemap();
if CanClose then
-- non-FIFO account, create a close market order
valuemap.OrderType = "CM";
valuemap.TradeID = trade.TradeID;
else
-- FIFO account, create an opposite market order
valuemap.OrderType = "OM";
end
valuemap.OfferID = trade.OfferID;
valuemap.AcctID = trade.AccountID;
valuemap.Quantity = trade.Lot;
valuemap.CustomID = trade.QTXT;
if trade.BS == "B" then valuemap.BuySell = "S"; else valuemap.BuySell = "B"; end
success, msg = terminal:execute(200, valuemap);
assert(success, msg);
end
function AsyncOperationFinished(cookie, successful, message)
if not successful then
core.host:trace('Error: ' .. message)
end
end
dofile(core.app_path() .. "\\strategies\\standard\\include\\helper.lua");