Description
This is the Random Entry System of Tom Basso as described in the Van Tharp Book Trade your way to Financial Freedom on page 253. In short, it consists of a random entry with a 3x "n" period Average True Range (ATR) (10 day recommended in book) stop loss that moves when the market moves in your favor or volatility shrinks. It also has a 1% of Account Balance volume calculator for each trade that can be turned off if a chosen volume quantity.
Parameters
- Symbol - As selected.
- Time frame - As selected, will be used for the ATR.
- Vol as % (Bool) - Yes to use calculated % of Account, No to use Volume Quantity (Vol Qty)
- Vol % (int) - 1 to use 1% of account per trade etc.
- Vol Qty (int) - Volume Quantity to be used for trade if Vol as % is set to No. Not used if Vol as % is set to Yes.
- Volatility Multiple (int) - Multiplies the ATR to calculate the stop loss. Eg 3 = 3 times the ATR for a stop loss.
- ATR Period (int) - The number of periods used on cBot selected Time Frame.
- SL Trigger "n"Pips (int) - Triggers the stop loss to try and update after "n pips, set by default to 2 pips just to limit multiple attempts to update stop loss in a short time frame. This means it has to move 2 pips in your direction before it tries to update the stop loss again.
- Allowable slippage (double) - Used directly in the ExecuteMarketOrder function to hold out on an order until the slippage is preferable.
The only thing you need to add to this cBot is time and markets (as many as you can afford). Most times this will do ok in the long run because it has a knack of catching the trend and letting it run and cutting short it's loser's. Test it out, recommended on the daily time frame as it minimizes churn and picks up on the larger flow of money rather than just the volatility.
Happy Trading.
Special Thanks to colga645 for % of account risk code and spka111 for ATR indicator.
using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;
namespace cAlgo
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class BassoREBot : Robot
{
[Parameter("Vol as %", DefaultValue = true)]
public bool riskBool { get; set; }
[Parameter("Vol %", DefaultValue = 1, MinValue = 1, Step = 1)]
public int volPercent { get; set; }
[Parameter("Vol Qty", DefaultValue = 1000, MinValue = 1000, Step = 1000)]
public int volQty { get; set; }
[Parameter("Volatility Multiple", DefaultValue = 3, Step = 1)]
public int volMultiple { get; set; }
[Parameter("ATR Period", DefaultValue = 10, Step = 1)]
public int atrPeriod { get; set; }
[Parameter("SL Trigger \"n\" Pips", DefaultValue = 2, Step = 1)]
public int unitSize { get; set; }
[Parameter("Allowable Slippage", DefaultValue = 0.5, MinValue = 0.5, Step = 0.1)]
public double marketRangePips { get; set; }
//Variables
private string MyLabel;
private bool initializing;
private bool ENPOpen;
private bool court;
private Random random = new Random();
private bool tradeType;
private TradeType _tradeType;
private double stopLossPips;
private int stepperCount;
private AverageTrueRange _atr;
protected override void OnStart()
{
//I am grateful for all that is given.
initializing = true;
Print("##### Initializing... #####");
MyLabel = string.Format("BassoREBot_{0}", Symbol.Code);
ENPOpen = true;
_atr = Indicators.AverageTrueRange(MarketData.GetSeries(this.TimeFrame), atrPeriod, MovingAverageType.Exponential);
initializing = false;
Print("##### Initializing Complete! #####");
}
protected int CalculateVolume()
{
Print("Calculating Volume!");
int volume;
switch (riskBool)
{
case true:
double costPerPip = (double)((int)(Symbol.PipValue * 10000000)) / 100;
double posSizeForRisk = (Account.Balance * volPercent / 100) / (stopLossPips * costPerPip);
double posSizeToVol = (Math.Round(posSizeForRisk, 2) * 100000);
Print("costperppip = {0}, posSizeFoprRisk = {1}, posSizeLotsToVol = {2}", costPerPip, posSizeForRisk, posSizeToVol);
volume = (int)Symbol.NormalizeVolume(posSizeToVol, RoundingMode.ToNearest);
Print("{0}% of Account Balance used for Volume! Volume equals {1}", volPercent, volume);
break;
default:
volume = volQty;
Print("Volume Quantity Used! Volume equals {0}", volume);
break;
}
return volume;
}
protected void ExecuteNewPosition()
{
ENPOpen = false;
Print("##### Preparing to execute new trade. #####");
_tradeType = RandomTrade();
Print("Generating {0} trade", _tradeType.GetType());
stepperCount = 0;
stopLossPips = (int)((double)_atr.Result.LastValue / Symbol.PipSize) * volMultiple;
int volume = CalculateVolume();
ExecuteMarketOrderAsync(_tradeType, Symbol, volume, MyLabel, stopLossPips, null, marketRangePips, ENPCallback);
}
protected void ENPCallback(TradeResult trade)
{
Position position = trade.Position;
tradeType = position.TradeType == TradeType.Buy ? true : false;
ENPOpen = true;
Print("##### Trade Executed! #####");
}
protected override void OnTick()
{
// Put your core logic here
var position = Positions.Find(MyLabel);
if (position == null)
{
if (ENPOpen && !initializing)
{
ExecuteNewPosition();
}
}
else
{
// ########## CORE LOGIC ########## //
double ball = tradeType ? (Symbol.Bid - position.EntryPrice) / Symbol.PipSize : (position.EntryPrice - Symbol.Ask) / Symbol.PipSize;
if (ball > 0)
{
court = true;
//Front Court or Trade is Positive.
}
else
{
court = false;
//Back Court or Trade is Negative.
}
//Print("Ball = {0}", ball);
switch (court)
{
case true:
// Were in Front Court
bool yard = false;
bool down = false;
int stepper = stepperCount * unitSize;
int nLinesMan = stepper + unitSize;
int sLinesMan = stepper - unitSize;
yard = ball > nLinesMan ? true : false;
down = ball < sLinesMan ? true : false;
if (yard)
{
stepperCount++;
AdjustStopLoss();
//Adjust stop loss if it is in our favour.
}
else if (down)
{
stepperCount--;
}
break;
case false:
// Were in Back Court
yard = false;
down = false;
stepper = stepperCount * unitSize;
nLinesMan = stepper + unitSize;
sLinesMan = stepper - unitSize;
yard = ball > nLinesMan ? true : false;
down = ball < sLinesMan ? true : false;
if (yard)
{
stepperCount++;
AdjustStopLoss();
}
else if (down)
{
stepperCount--;
}
break;
}
}
}
private void AdjustStopLoss()
{
double calcStopLoss = ((double)_atr.Result.LastValue / Symbol.PipSize) * volMultiple;
double newStopLossPrice = tradeType ? Symbol.Bid - calcStopLoss * Symbol.PipSize : Symbol.Ask + calcStopLoss * Symbol.PipSize;
Position position = Positions.Find(MyLabel, Symbol);
bool modify = tradeType ? newStopLossPrice > position.StopLoss : newStopLossPrice < position.StopLoss;
if (modify)
{
ModifyPosition(position, newStopLossPrice, position.TakeProfit);
Print("Stoploss updated for {0}", position.SymbolCode);
}
}
private TradeType RandomTrade()
{
return random.Next(2) == 0 ? TradeType.Buy : TradeType.Sell;
}
protected override void OnStop()
{
Position position = Positions.Find(MyLabel);
if (position != null)
ClosePosition(position);
}
}
}
Jonkey
Joined on 09.11.2015
- Distribution: Free
- Language: C#
- Trading platform: cTrader Automate
- File name: BassoREBot.algo
- Rating: 0
- Installs: 2920
- Modified: 13/10/2021 09:54
Comments
The name of this cBot should be "The Account Destroyer".
Hi Jonkey, would you like to share some back-test or demo account results for this bot?