Today, I'm very happy to present a guest post from Omar Hafez. He contacted me via email and as we got to chatting, I was really interested in what he is doing with the FXCM Trading Station platform. Almost everyone in Forex (including me), talks about Metatrader…some people talk about Ninjatrader and a few people mention TradeStation now and again.
But I was surprised how much flexibility there is in the FXCM desktop platform and how powerful it can be. The best part is that it uses an already established programming language (instead of something dodgy like EasyLanguage), which is easier to learn than most.
On top of that, FXCM makes it possible for developers to sell their code in the FXCM version of an “app store,” something that Omar will get into in the second part of this series. Anyway, Omar does a better job of explaining this than me.
So without further delay, here is Omar…
My name is Omar Hafez from MooMooFX and today I'm going to give you a quick intro into developing for the FXCM Trading Station platform.
Firstly, a little bit of background on me and why I chose FXCM as my platform. I'm a Software Engineer with over 10 years of experience working with large corporations – 7 of those 10 years with the investment banks. One day, I decided that I had what it takes to write my own automatic algorithmic trading system, and decided to concentrate on this full-time.
I tried many broker's software and systems with a focus on those that provided demo accounts, and many of them were very good for day traders, but didn't cut it for those interested in developing their own indicators and systems.
While MetaTrader and Ninjatrader are very popular, and their online community is huge (although IMO mostly junk), I ended up settling with FXCM's systems. FXCM's Strategy Backtester and Optimizer tools are amazing and easily superior to other systems. The underlying language, Lua, is easy to pick up and there is a large amount of documentation and an active forum to support you.
Today just to show you how easy it is we are going to re-create, from scratch, a simple implementation of the famous MACD oscillator indicator together. As we go through, I'll keep things simple and brief while linking to online documentation for further reading for those interested.
But first, if you want to do this yourself then you'll need the following prerequisites:
- A little bit of an programming experience or interest to learn
- Download and install the Indicore SDK
- FXCM Trading Station (if you want to see your indicator in action)
So, let's begin.
The most basic structure of an indicator contains only three functions.
- The Init() function to initialize the indicator profile – general data on the indicator.
- The Prepare() function to initialize a particular instance of the indicator.
- The Update() function that performs the number crunching each time there is an update to the price data.
Alright, so firstly let's look at the Init() function. The whole point of this function is to basically define or configure our indicator's profile, amongst all the other indicators that are available. So we will need to define things that are specific to this indicator, but general to all instances of this indicator, such as its name, what type of data it needs, and it's configuration parameters.
For those not familiar with the term instance in this context. Just imagine you add the MACD indicator twice on the same chart that differ in their moving average lengths, this would mean you have two different instances of the MACD indicator.
The Init() Function
Alright, so let's create the Init() function and add some of the stuff mentioned above.
Note: Two minus signs, –, represents a comment which has no impact on the source of the indicator but is useful to add comments or notes into code. I will use them to explain each item we are doing.
function Init() -- the name of our indicator, let’s call it Simple MACD indicator:name("Simple MACD"); -- the description of our indicator indicator:description("Our implementation of the Simple MACD Indicator"); -- can it accept Tick data or must it be Bar data? This should work on Tick and Bar data so let’s use Tick data for now. indicator:requiredSource(core.Tick); -- Is this going to be an Indicator on top of the Price Chart, or an Oscillator under the Price Chart. indicator:type(core.Oscillator); -- How do I want to categorize this indicator compared to other indicators. indicator:setTag("group", "My Own Stuff");
Ok good. The above is pretty standard and required for all Indicators we develop. However, we aren't finished with the Init() function yet. We need to define some parameters that we can configure, such as, how long the moving averages should be, and perhaps what color we want the lines to be etc.
So adding more…
-- introduce a new grouping for my parameters related to calculating the value indicator.parameters:addGroup("Calculation Parameters"); -- add some number parameters to capture the length of the MAs and Signal line (Parameter Name, Display Name, Description, Default, Min, Max) indicator.parameters:addInteger("shortLength", "Short MA", "The length of the short moving average", 12, 2, 1000); indicator.parameters:addInteger("longLength", "Long MA", "The length of the long moving average", 26, 2, 1000); indicator.parameters:addInteger("sigLength", "Signal", "The length of the signal line", 9, 2, 1000); -- introduce a new grouping for my parameters related to styling indicator.parameters:addGroup("Style"); -- add some color parameters that can be used to style each of the lines (Parameter Name, Display Name, Description, Default Color in RGB values) indicator.parameters:addColor("MACD_color", "MACD Color", "Color of the MACD Line", core.rgb(255, 0, 0)); indicator.parameters:addColor("Signal_color", "Signal Color", "Color of the Signal Line", core.rgb(0, 0, 255)); indicator.parameters:addColor("Histogram_color", "Histogram Color", "Color of the Histogram Line", core.rgb(0, 255, 0)); end
And we're done with Init()! So you can see we have now added a few parameters we can use to tweak the calculation, and a few parameters so we can tweak the style of each instance.
The Prepare() Function
Now we only need to write the Prepare() function, where we actually use the configuration we just defined to set up our instance, and then we write the number crunching part when the price changes.
So just as before, let's define a Prepare() function, and a quick validation of our parameter values.
function Prepare() -- Check to make sure the long moving average is indeed longer than the short moving average... -- So if the longLenth parameter is less than or equal to the shortLenth parameter, throw an error with an appropriate message. if (instance.parameters.longLength <= instance.parameters.shortLength) then error("The short moving average length must be smaller than long moving average length"); end
OK, once we pass the validation section, let's set the name of this specific instance to something unique to this instance's configuration so we can distinguish it apart from other ones. We can access the currency pair source using instance.source so let's use that, and add the lengths of the moving averages.
-- Create a local variable to store the name of this instance that has the name of the currency pair, and the configuration values. local name = profile:id() .. "(" .. instance.source:name() .. ", " .. instance.parameters.shortLength .. ", " .. instance.parameters.longLength .. ", " .. instance.parameters.sigLength .. ")"; -- Set the name of our instance to the above name variable. instance:name(name);
Next, we need to tell the system what type of data we will be creating. This is done through data streams.
In this case, we will need to create three streams. Two of them will be of type Line (the MACD Line and the Signal Line) and one will be of type Bar (the Histogram).
Also, the system needs to know the color for the stream (our chance to use the color parameters we defined in the Init() function) and we need to tell it when the first data is expected. More on streams here.
-- Create the output streams required. (Stream Name, Stream Type, Full Name, Label, Color, First Period) local MACD = instance:addStream("MACD", core.Line, name .. ".MACD", "MACD", instance.parameters.MACD_color, instance.parameters.longLength); local SIGNAL = instance:addStream("SIGNAL", core.Line, name .. ".SIGNAL", "SIGNAL", instance.parameters.Signal_color, instance.parameters.longLength + instance.parameters.sigLength); local HISTOGRAM = instance:addStream("HISTOGRAM", core.Bar, name .. ".HISTOGRAM", "HISTOGRAM", instance.parameters.Histogram_color, instance.parameters.longLength + instance.parameters.sigLength);
Oh no wait, that's not right.
If we define the streams inside a function then we cannot access them outside. We need to be able to access these streams in the Update() function later so we can update their values!
Also, we will need to use the value of when the first data is expected, so let's also turn this into a global variable (so we can access it from any function). Let's re-write the last few lines like so and finish up the Prepare() function.
-- Create the output streams required. (Stream Name, Stream Type, Full Name, Label, Color, First Period) macdFirst = instance.parameters.longLength; signalFirst = instance.parameters.longLength + instance.parameters.sigLength; MACD = instance:addStream("MACD", core.Line, name .. ".MACD", "MACD", instance.parameters.MACD_color, macdFirst); SIGNAL = instance:addStream("SIGNAL", core.Line, name .. ".SIGNAL", "SIGNAL", instance.parameters.Signal_color, signalFirst); HISTOGRAM = instance:addStream("HISTOGRAM", core.Bar, name .. ".HISTOGRAM", "HISTOGRAM", instance.parameters.Histogram_color, signalFirst); end
Let's not forget to define the global variables outside the function. So, just above but function Prepare() line, but after the end of the Init() function, let's add the following:
local macdFirst; local signalFirst; local MACD = nil; local SIGNAL = nil; local HISTOGRAM = nil;
OK great! We are almost done.
The Update() Function
Lastly, let's tie it all together in the Update() function and do the actual calculations! So for those who don't know, the MACD indicator actually calculates two moving averages, then takes the difference between these moving averages and plots this as the MACD Line.
Additionally, another moving average is taken of the MACD line, and this is called the Signal Line. Lastly, a histogram is created by taking the difference between the MACD Line and Signal Lines. So, let's move on!
The Update() function, unlike the Prepare() and Init() functions, comes with two inputs – the period and mode. For the purpose of this example I'm going to ignore mode, and describe period as simply being an instance in time.
So if we wanted to represent the time as when our instance is created as T=0, then let's say period=0. If we need 10 ticks, or 10 bars of data, then we cannot calculate anything until period is greater than or equal to 10. So, lucky for us we already calculated when our data is available above.
The Update() function should start off a little something like this…
function Update(period, mode) -- check to make sure we have enough data to calculate the moving averages, and hence the MACD line. if (period >= macdFirst) then -- calculate the short moving average by taking the average of the source data from shortLength periods ago to the current period. local shortMA = mathex.avg(instance.source, period - instance.parameters.shortLength + 1, period); -- calculate the long moving average by taking the average of the source data from longLength periods ago to the current period. local longMA = mathex.avg(instance.source, period - instance.parameters.longLength + 1, period); -- calculate and set the MACD Line value for this period as being the difference between the Short MA value and the Long MA value. MACD[period] = shortMA - longMA; end
OK, so getting a little bit more complex now but I hope you can see what we've done here. By taking the average of the price data represented by instance.source, for specified ranges, we can calculate the two moving averages we need. Then, by differencing them, we have now calculated one of the three output streams we need!
So let's finish this!
-- check to make sure we have enough data to calculate the signal line, and therefore we can also calculate the histogram. if (period >= signalFirst) then -- calculate the moving average of the MACD line for the required length of periods as defined by the signal length, until the current period. SIGNAL[period] = mathex.avg(MACD, period - instance.parameters.sigLength + 1, period); -- calculate and set the Histogram value as the difference between MACD and the Signal lines. HISTOGRAM[period] = MACD[period] - SIGNAL[period]; end end
And that's it! Save the file as SimpleMACD.lua and we're done! If you had been following closely, the complete source code should look something like this (sample file download in zip format).
You could now load that into the Lua Indicator Debugger application that came with the IndicoreSDK, and run it against some test data. You should get something that looks like this.
If it runs fine in the debugger, then you can import it into FXCM Trading Station and start using it to help your trading. You'll need to have FXCM Trading Station installed and a valid account (live or demo) to see it run, but if you do it should look something like this!
And there we have it – our very own custom indicator, in this case an oscillator under the price chart.
I hope you enjoyed this quick tutorial. Of course we've only completed a simple implementation of the MACD indicator.
You could expand this to calculate Exponential Moving Averages instead, or add some more funk to your indicator. That's the beauty of being able to write code, you can get the computers to work for you!
Next steps would be to write an Automated Strategy that generates trade signals from your custom indicator (when the lines cross or whenever you want!). Then you could sit back and watch your system trade by itself, or let it trade while you are sleeping.
Writing a strategy isn't that much harder than what we have done today!
If you are interested, there's a lot more information online and you can do some pretty cool stuff. Most information is on FxCodeBase, either in the Custom Indicators Forum or on the Wiki/SDK links.
Here are some more links to help you on your way:
- How to Start Developing Your Own Indicator
- First Simple Indicator
- Signals and Strategies
- Simple Strategy – Moving Average Cross
- Recommended Reading
If you have any questions for Omar so far, ask them in the comments below.
Editors note: I would really like to thank Omar for taking the time to create this tutorial. Keep an eye out for part two, where he talks about selling your code in the FXCM App Store.