Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating a Mechanical Trading System Part 2: Four Percent Trading System

0.00/5 (No votes)
17 Aug 2006 1  
An article describing how to implement and test a mechanical trading system such as Ned Davis 4 percent model using a C# simulator.
Sample Image - Trading_System.jpg

Introduction

The first article in this series introduced a project to code mechanical trading system robots for the .NET platform using the C# language. This article introduces how to code a trading system that will watch price action and make buying or selling decisions. It is the aim of this project to write a program that does not depend on any underlying platform to execute, but rather receives price data directly from a broker and places orders via an API.

The code provided here is part of a larger project to create a backtesting platform and an automated FOREX trading robot. Source code for this project is available here.

Background

This article implements the 4 percent model, developed by Ned Davis and popularized by Martin Zweig in his book Winning on Wall Street. This is a very simple trend following model that will put you on the right side of a sustained trend. This is not to say that this system does not have weaknesses. If too small of a percentage is chosen, the system can whipsaw, or execute many small losing trades.

This system makes an initial buying or selling decision depending on whether the prices rise or fall more than a certain percentage from the first price seen. Once the initial direction is set, the system reverses its position when the prices retrace a certain percentage from a local maximum or minimum value. For example, if the system is buying, and the price rises to a maximum of 104, then retraces back to 100, the system will then close the long position and sell to establish a short position.

This is a reversal system and it is continuously in the market. To fully utilize its potential, it must be implemented on a market that allows traders to go long or short with equal ease. The futures, options, and Forex markets all possess this property.

This project implements the random walk algorithm to generate simulated price data. The algorithm is shown in the boxes below. A virtual coin is flipped each time the function is called. If heads, the price increases by 1c, if tails, the price decreases by 1c. This random data is then sent to the account manager and to the trading system. The trading system makes a decision and notifies the account manager if it wants to buy or sell. The account manager then outputs the trade log to the screen.

Simulated Trading Example

The screenshot at the beginning of this article shows a run of the algorithm through random walk data. The price starts at 100 and starts randomly moving up and down by 1c. Since 100 is going to be the fist price, the first decision will be to buy if prices rise by 4% to 104, or sell if prices fall by 4% to 96. In this example, the 96 price target was hit first and the system places a sell order. The system is now looking for new lows, if prices retrace from this new low by 4%, then the system closes the sell order and places a buy order. In this example, the system reverses position at 98.01 as shown, since the prices must have come down to 94.08 (4% below 98.01). In the hypothetical example shown, after adding the transactions shown in the screen, the system shows a profit of 4.92. This very simple system will put you on the right side of a sustained trend, but is susceptible to whipsaws if the percentage is not appropriate for the width of the oscillations between maximums and minimums. Perhaps use of the ATR or the ADX indicators could reduce the whipsaws.

Using the Code

This project has been arranged so that an unmodified trading system taken from the 4xlab.net project executes successfully. The supporting class framework has been implemented in a minimalistic way to allow this system to output trading decisions. Features such as parameter optimization and end of trading reports containing gains, loses and drawdown figures are not available in this project.

The FourPercentModel.cs file contains a complete implementation for a trading system. The fPercentageChange class inherits from the ForexObject. This allows the framework to receive and execute trading systems by treating them as their base class (ForexObject).

The base class ForexObject defines the following methods:

  • public override void Init(string pParameters) 

    This method initializes the object. This was chosen instead of the constructor to allow an existing object to be re-initialized and allow the object to remember multiple runs with different parameters for optimization purposes.

    The system is initialized by sending it a parameter string in which parameter:value pairs are separated by semicolons. If no parameters are sent, the object is responsible for selecting default values.

  • public override void ReceiveTick(sTick pTick) 

    The following two methods are used by the object to receive real time quote data. If the object wants to receive tick data, it will register itself with the framework as a tick listener. The private method DecisionFunction must be called either here, or in the ReceiveCandle method.

    The DecisionFunction method is responsible for analyzing quote data and making buying or selling decisions executed through the Account object. In this example, it can be seen that the decision function involves looking for new minimums or new maximums depending on the current trading direction. Once prices retrace by a specified percentage, the system reverses direction. Trades are entered by calling Framework.Account.EnterTrade(int pDirection). For buy orders, use +1 for direction, use -1 for sell orders. The Account object returns its trading status via the Framework.Account.InTrade() method. A positive value means a buy order is open; a negative value means a sell order is open. Zero is returned if there are no trades open.

  • public override void ReceiveCandle(sCandle pCandle, int pPeriod, string pCBTitle) 

    This method is used by the object to receive candle data. To accomplish this, the object must register itself with the framework as a candle listener.

  • public override bool Finished() 

    This method allows the object to be called several times by the framework. The object can choose to change quote data after the end of each run and compare runs internally. If so, the first time the object is finished receiving quote data and this method is called, the object switches quote data and returns false. The second time around, this method will return true.

  • public override string Title() 

    This method allows individual objects in a collection to be identified by a friendly name.

File: FourPercentModel.cs

//
// 4X Lab.NET © Copyright 2005-2006 ASCSE LLC
// http://www.4xlab.net
// email: 4xlab@4xlab.net
//

using System;

namespace FourPercentShell
{
    // UNMODIFIED CLASS, SAME AS IN 4XLAB.NET    
    public class fPercentageChange : ForexObject
    {
    double iPercentage;
    double iMin;
    double iMax;

    bool   FirstTick;
    sTick  Tick;
    double Spread;

    public fPercentageChange()
    {
    }

    public override void Init(string pParameters)
    {
        this.InitializeParameters(pParameters,"PERCENT:0.001;");
        iPercentage = PParser.GetDouble("PERCENT",0);
        Framework.TickServer.RegisterTickListener("System","*",this);
        Framework.WriteGraphLine("InTrade,Margin,Tick,Min,MinTh,Max,MaxTh");
        FirstTick = true;
    }

    public override void ReceiveTick(sTick pTick)
    {
        Spread    = pTick.Ask - pTick.Bid;
        Tick    = pTick;

        DecisionFunction();
    }

    public override void ReceiveCandle(sCandle pCandle, int pPeriod, string pCBTitle)
    {
    }
    
    private void DecisionFunction()
    {
        if (FirstTick)
        {
        FirstTick = false;
        iMin = Tick.Bid;
        iMax = Tick.Bid;
        }
        else
        {
        if (Framework.Account.InTrade()==1)
        {
            if (Tick.Bid > iMax)
            {
            iMax = Tick.Bid;
            }
            
            if ((iMax-Tick.Bid) > iPercentage*iMax)
            {
            Framework.Account.ExitTrade();
            Framework.Account.EnterTrade(-1);
            iMin = Tick.Bid;
            iMax = 0;
            }
        }

        if (Framework.Account.InTrade()==-1)
        {
            if (Tick.Bid < iMin)
            {
            iMin = Tick.Bid;
            }
            
            if ((Tick.Bid-iMin) > iPercentage*iMin)
            {
            Framework.Account.ExitTrade();
            Framework.Account.EnterTrade(1);
            iMax = Tick.Bid;
            iMin = 0;
            }
        }

        if (Framework.Account.InTrade()==0)
        {

            if ((Tick.Bid-iMin) > iPercentage*iMin)
            {
            Framework.Account.EnterTrade(1);
            iMax = Tick.Bid;
            iMin = 0;
            }

            if ((iMax-Tick.Bid) > iPercentage*iMax)
            {
            Framework.Account.EnterTrade(-1);
            iMin = Tick.Bid;
            iMax = 0;
            }
        }
        }

        double logInTrade = Framework.Account.InTrade();
        double logMargin  = Framework.Account.GetAccount().C;

        double MinTh = iMin + iMin*iPercentage;
        double MaxTh = iMax - iMax*iPercentage;

        Framework.WriteGraphLine(logInTrade+","+logMargin+","+Tick.Bid+","+
                     iMin+","+MinTh+","+iMax+","+MaxTh);
    }

    public override bool Finished()
    {
        bool returnv = true;

        return returnv;
    }

    public override string Title()
    {
        return "% Trend Change";
    }
    }
}

The following file shows how the Tick Server generates simulated price data based on the random walk model. First, the price is incremented or decremented depending on a random number. Then a Tick object is created with Bid and Ask prices. Afterwards, the tick is sent first to the account simulator and to the registered tick listener later. The code implemented here only allows for one registered tick listener in addition to the account simulator.

File: Framework.cs

    public class oTickServer
    {
    
    ...

    public void GenerateTick()
    {
        int randomint = (int)(RNG.NextDouble()*1000);

        if (randomint>500)
        {
        price++;
        }
        else
        {
        price--;
        }

        Tick = new sTick();

        Tick.Bid = price;
        Tick.Ask = Tick.Bid + 0.01;

        Framework.Account.ReceiveTick(Tick);
        tl.ReceiveTick(Tick);
    }

    ...

    }

Read More About It

History

  • 17th August, 2006: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here