How to draw the difference between two moving averages as a histogram on H1 time frame in MQL4? - algorithmic-trading

This is my trial to draw the difference between two moving averages as a histogram on H1 time frame. The problem is that it does not change when I change a time frame, especially to lower ones. I am a beginner at MQL4, with no experience or programming background, so please explain to me the mistake.
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_color1 Gray
#property indicator_width1 2
extern int maperiod1 = 25;
extern int maperiod2 = 55;
extern int timefr = 60;
double mainbuffer[];
int init(){
SetIndexBuffer( 0, mainbuffer );
SetIndexStyle( 0, DRAW_HISTOGRAM );
return(0);
}
int start(){
int counted_bars = IndicatorCounted();
if ( counted_bars < 0) return(-1);
if ( counted_bars > 0) return( 0);
int limit = ( Bars - counted_bars );
for ( int i = limit; i >= 0; i-- ){
int shift = iBarShift( NULL, timefr, Time[i] );
double maB = iMA( NULL, timefr, maperiod1, 0, MODE_EMA, 0, shift );
double maR = iMA( NULL, timefr, maperiod2, 0, MODE_EMA, 0, shift );
mainbuffer[shift]= ( maB - maR );
}
return(0);
}

In your code you calculate MA of H1 timeframe, that is why there's no difference what happens on smaller timeframes - it checks H1 only.
if (counted_bars>0) return(0);
- as far as i can remember, if number of counted bars if positive, you need to decrease it by one in order to recalculate previously known bar: if (counted_bars>0) counted_bars--;

Besides the "immunity" to any TimeFrame selection,the code does not provide means to update in sync with Time[] properly
As Daniel has already noted above the "rigid", hard-coded relation to an extern pre-selected time-frame ( which technically ought be designed as rather some enum:
enum ENUM_GUI_SELECT_TFRAME {
M1 = PERIOD_M1,
M5 = PERIOD_M5,
...
..
.
H1 = PERIOD_H1,
H4 = PERIOD_H4,
...
..
.
MN = PERIOD_MN
};
// ++++------------------------------------------ MAKES SURE,
// |||| ALWAYS ONLY
// |||| PROPER VALUES GET
// vvvv EVER SELECTED
extern ENUM_GUI_SELECT_TFRAME timefr = PERIOD_H1;
) there are some further points to solve with code-activation / execution barriers.
What prevents the code from proper updates?
This tandem of conditions:
if ( counted_bars < 0) return(-1);
if ( counted_bars > 0) return( 0);
is met if and only if the IndicatorCounted() == 0, never more.
IndicatorCounted() ~ The function returns the amount of bars not changed after the indicator had been launched last.
So the Custom Indicator works as a devil during the first call, where there were all bars "changed" since the ( non-existent ) previous call. After that, until a newly created bar, the Indicator gets called with having passed the condition because the IndicatorCounted() == 0, but no new bars have yet appeared -- which would thus make the IndicatorCounted() == 1 and the rest of the code of the Custom Indicator will not get executed -- which seems rather the very mirrored logic, the Custom Indicators were equipped with to avoid non-productive loops in case no new bars were created, but to get an immediate update in case a new bar has started.
In other words, even in case the IndicatorCounted() == 0 the last ( hot ) value [0] could still and does change, so if and only if the Custom Indicator value is somehow dependent on this actual ( still changing ) live-value of Close[0], it makes due sense to update the [0] cell of the mainbuffer[].
If it is not dependent on Close[0], it makes no sense to run the code, as it would not update anything.

Related

How to "tie" 3 RSI-indicators to one Bollinger Band, using IMAonArray()?

There are 3 RSI indicators, each having its own period.
I want to tie all 3 to one Bollinger Band.
Tell me how to do this better?
for(i=limit; i>=0; i--) {
ma=iMAOnArray(RSI,0,bb_period,0,0,i); // midle
stdev=iStdDevOnArray(RSI,0,bb_period,0,0,i); // dev
BBUP[i]=ma+bb_dev*stdev; // up
BBDOWN[i]=ma-bb_dev*stdev; // down
Buff4[i]=0;
Buff5[i]=0;
}
if(limit<Bars-1) limit++;
for(i=limit; i>0; i--) {
if(PrevSignal >= 0) {
if( RSI[i] < BBDOWN[i]
&& RSI[i+1] < BBUP[i+1]
&& RSI2[i] < BBDOWN[i]
&& RSI2[i+1] < BBUP[i+1]
&& RSI3[i] < BBDOWN[i]
&& RSI3[i+1] < BBUP[i+1]
) {
Buff4[i] = Low[i]-5*MarketInfo(Symbol(),MODE_POINT); // MathMin(BBDOWN[i],WPR[i]); // Low[i]-5*MarketInfo(Symbol(),MODE_POINT);
PrevSignal = -1;
}
}
}
I want that when all 3 RSI were below BB, then the signal
https://i.stack.imgur.com/fKff7.jpg
The code AS-IS could be designed with 4x less overhead:
Even before O/P will define the target ( how to tie three values into one ), one might have already detected the code-blocks are sync-stepped / aligned in [i]-indexing, not peeking into the future, non-intervening each to the other's values ( princially indeendent ) and thus redundant, so one could safely save a lot from these "shared" overheads:
int i;
double ma,
stdev;
for(i=limit; i>=0; i--) { RSI[ i] = iRSI( Symbol(), Period(), rsi_period, PRICE_CLOSE, i );
RSI2[i] = iRSI( Symbol(), Period(), rsi_period_2, PRICE_CLOSE, i );
RSI3[i] = iRSI( Symbol(), Period(), rsi_period_3, PRICE_CLOSE, i );
/* the "last" for(){...} code-block body is not included,
until the calculus of how to tie three vectors
into a common Bollinger Band is defined by O/P,
but
could fit in this common "stream"-processing too
*/
}
The O/P code has been remarkably changed - may check the revisions
Nota bene: ad-hoc the presented update
Given the Quantitative Finance heritage, from J. Welles Wilder, the author of the said RSI model, the definition says:
Relative Strength Index (RSI) is a momentum oscillator that measures the speed and change of price movements. RSI oscillates between zero and 100.
It turns out, that any attempt to mix apples and oranges leads to just a principal confusion. Taking the apples == the Bollinger Band ( having a clear PriceDOMAIN dimension [CCY2] ) and trying to mix it ( additively, using { ADD | SUB } operations ) with oranges == RSI ( a dimension-less relative / percent indicator ) will yield an uninterpretable result ( has a hidden superposition + scaling uncertainties ).
Still in doubts?
Just let's take XAUUSD for a second. It's Bollinger Band values, bearing in mind any reasonable multiples of sigma ( StDev ), the RSI will always be under the lower-band ... thus such construct will have zero information value, as:RSI-<0..100> << BollingerBandLOWER-( 1257.000 - ( N * sigma ) )
So a model ought be revised, so as to have some Quantitative Finance support built-in. Mixing apples with oranges simply will not help in any serious business sense.

How can I transform the code I wrote down below?

I am suppose to code the snake game in java with processing for IT classes and since I had no idea how to do it I searched for a YouTube tutorial. Now I did find one but he used the keys 'w','s','d','a' to move the snake around - I on the other hand want to use the arrow keys. Could someone explain to me how I transform this code:
if (keyPressed == true) {
int newdir = key=='s' ? 0 : (key=='w' ? 1 : (key=='d' ? 2 : (key=='a' ? 3 : -1)));
}
if(newdir != -1 && (x.size() <= 1 || !(x.get(1) ==x.get(0) + dx[newdir] && y.get (1) == y.get(0) + dy[newdir]))) dir = newdir;
}
into something like this:
void keyPressed () {
if (key == CODED) {
if (keyCode == UP) {}
else if (keyCode == RIGHT) {}
else if (keyCode == DOWN) {}
else if (keyCode == LEFT) {}
}
This is my entire coding so far:
ArrayList<Integer> x = new ArrayList<Integer> (), y = new ArrayList<Integer> ();
int w = 900, h = 900, bs = 20, dir = 1; // w = width ; h = height ; bs = blocksize ; dir = 2 --> so that the snake goes up when it starts
int[] dx = {0,0,1,-1} , dy = {1,-1,0,0};// down, up, right, left
void setup () {
size (900,900); // the 'playing field' is going to be 900x900px big
// the snake starts off on x = 5 and y = 30
x.add(5);
y.add(30);
}
void draw() {
//white background
background (255);
//
// grid
// vertical lines ; the lines are only drawn if they are smaller than 'w'
// the operator ++ increases the value 'l = 0' by 1
//
for(int l = 0 ; l < w; l++) line (l*bs, 0, l*bs, height);
//
// horizontal lines ; the lines are only drawn if they are smaller than 'h'
// the operator ++ increases the value 'l = 0' by 1
//
for(int l = 0 ; l < h; l++) line (0, l*bs, width, l*bs);
//
// snake
for (int l = 0 ; l < x.size() ; l++) {
fill (0,255,0); // the snake is going to be green
rect (x.get(l)*bs, y.get(l)*bs, bs, bs);
}
if(frameCount%5==0) { // will check it every 1/12 of a second -- will check it every 5 frames at a frameRate = 60
// adding points
x.add (0,x.get(0) + dx[dir]); // will add a new point x in the chosen direction
y.add (0,y.get(0) + dy[dir]); // will add a new point y in the chosen direction
// removing points
x.remove(x.size()-1); // will remove the previous point x
y.remove(y.size()-1); // will remove the previous point y
}
}
It's hard to answer general "how do I do this" type questions. Stack Overflow is designed for more specific "I tried X, expected Y, but got Z instead" type questions. That being said, I'll try to answer in a general sense:
You're going to have a very difficult time trying to take random code you find on the internet and trying to make it work in your sketch. That's not a very good way to proceed.
Instead, you need to take a step back and really think about what you want to happen. Instead of taking on your entire end goal at one time, try breaking your problem down into smaller steps and taking on those steps one at a time.
Step 1: Can you store the state of your game in variables? You might store things like the direction the snake is traveling the location of the snake, etc.
Step 2: Can you write code that just prints something to the console when you press the arrow keys? You might do this in a separate example sketch instead of trying to add it directly to your full sketch.
Step 3: Can you combine those two steps and change the state of your sketch when an arrow key is pressed? Maybe you change the direction the snake is traveling.
The point is that you need to try something instead of trying to copy-paste random code without really understanding it. Break your problem down into small steps, and then post an MCVE of that specific step if you get stuck. Good luck.
You should take a look into Java API KeyEvent VK_LEFT.
And as pczeus already told you, you need to implement a capturing of the keystrokes! This can be checked here (Link from this SO answer).

Trouble implementing North-East Paths with recursion

I'm supposed to use recursion to output the total number of unique north-east paths ne(x, y) to get from point A to point B, where B is x rows north and y columns east of A. In addition, I am required to print the possible unique NE paths.
I know how to use recursion to get the total number of unique paths. However, I am stuck with using recursion to print all the NE paths correctly.
This is the given output of some test cases:
image of output
Anyway, here's a screenshot of my faulty recursive code.
Please do give me advice where I went wrong. I have been burning a lot of time on this, but still I can't reach a solution.
I think you should print if( rows == 0 && cols == 0 ), because it's the case when you've reached point B.
Why are you using path+="N" in the first ne call in return? this will add "N" to original path and then you'll get path+"N"+"E" in the second call.
Try following:
public static int ne( int rows, int cols, String path )
{
if( rows == 0 && cols == 0 )
{
System.out.println(path);
return 1;
}
int npats = 0, wpaths = 0;
if( rows != 0 )
npaths = ne( rows-1, cols, path+"N" );
if( cols != 0 )
wpaths = ne( rows, cols-1, path+"E" );
return npaths + wpaths;
}

How to Quantise an indicator in MQL4?

I am failing over and over trying to get this indicator to run quantised with 2 buffers in mql4. After a long time reading I have put 2 extra buffers in to squish it :/ because:
the indicator is sitting between 0.1430-0.1427 at present but doesn't have a fixed top and bottom.
I can't seem to suss it; cool indicator but won't play fair!
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_color1 Lime
#property indicator_color2 Red
#property indicator_color3 CLR_NONE
#property indicator_color4 CLR_NONE
//#property indicator_minimum 0
//#property indicator_maximum 100
extern int P = 13;
extern int T = 3000;
extern double P2 = 0.001;
//int MIN = 0;
//int MAX = 100;
double G[];
double R[];
double B3[];
double B4[];
int init(){
IndicatorBuffers(4);
SetIndexBuffer( 0, G );SetIndexStyle( 0, DRAW_LINE, STYLE_SOLID, 1, Lime );
SetIndexBuffer( 1, R );SetIndexStyle( 1, DRAW_LINE, STYLE_SOLID, 1, Red );
SetIndexBuffer( 2, B3 );SetIndexStyle( 2, DRAW_NONE );
SetIndexBuffer( 3, B4 );SetIndexStyle( 3, DRAW_NONE );
return(0);
}
int start(){
if ( T >= Bars ) T = Bars;
SetIndexDrawBegin( 0, Bars - T + P + 1 );
SetIndexDrawBegin( 1, Bars - T + P + 1 );
SetIndexDrawBegin( 2, Bars - T + P + 1 );
SetIndexDrawBegin( 3, Bars - T + P + 1 );
int Z, C, Opt = IndicatorCounted();
if ( Bars <= 38 ) return(0);
if ( Opt < P ){
for ( Z = 1; Z <= 0; Z++ ) G[T-Z] = 0.0;
for ( Z = 1; Z <= 0; Z++ ) R[T-Z] = 0.0;
}
Z = T - P - 1;
while( Z >= 0 ){
double A, S1, S2;
S1 = 0.0; for ( C = 0; C <= P - 1; C++ ){ S1 = S1 + ( High[Z+C] + Low[Z+C] ) / 2;}
S2 = 0.0; for ( C = 0; C <= P - 1; C++ ){ S2 = S2 + ( ( High[Z+C] + Low[Z+C] ) * ( C+1 ) / 2 );}
A = S1 / S2;
// if ( A < MIN ){ MIN = A;}
// if ( A > MAX ){ MAX = A;}
// A = ( MIN / MAX ) * 100;
G[Z] = A;
if ( Z > 0 ){ R[Z-1] = A;}
Z--;
}
for ( int N = T-P-2; N >= 0; N-- ){
if ( N > 0 ){
if ( G[N-1] > G[N] ){ R[N] = EMPTY_VALUE; continue;}
if ( G[N-1] < G[N] ){ G[N] = R[N]; continue;}
}
B3[0] = G[0] + P2;
B4[0] = G[0] - P2; //forced quantise using 2 extra buffers
}
return(0);
}
Let´s split the task first
0) indicator logic
1) indicator quantisation step
2) indicator performance
MQL4 Custom Indicator programming relies on deeper understanding of underlying MetaTrader4 Terminal platform. Each external Market Event, changing a traded instrument price, is signalled to a lcoalhost by a network delivery of a QUOTE ... message from MetaTrader4 Server. This is aka Tick and it triggers a call to a function originally called start(), in newer New-MQL4.56789 renamed to OnTick().
The below modified MQL4 listing contains remarks for core-logic disambiguation, which must precede all the below listed steps.
1) indicator quantisation step
While the code is still very inefficient ( as per [2] below ) the logic does not include any straight hurdle form having the output quantised to any form thereof { binary | ternary | arbitrary-number-of-states }-quantised system. Whence the indicator core-logic is cleared, the quantisation step is just a trivial conversion from R(1) to I(1).
2) indicator performance
Any Tick arrival may, but need not modify either High[0] or Low[0], which are the only variable parts of the proposed Custom Indicator calculus.
This is the core idea on how to reduce the scope of re-calculations, that the MQL4 code has to realise per tick. In recent versions of MT4, all Custom Indicators share a single thread, the more stress has been put on efficient algorithmisation of Custom Indicators, at these may block the platform's trading decisions on poor, inefficient code-loops and convolution/recursion re-executions.
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_color1 Lime
#property indicator_color2 Red
#property indicator_color3 CLR_NONE
#property indicator_color4 CLR_NONE
extern int P = 13;
extern int T = 3000;
extern double P2 = 0.001;
double G[]; // 0: LINE
double R[]; // 1: LINE
double B3[]; // 2: BUT NEVER PAINTED, NEVER CONSUMED _?_
double B4[]; // 3: BUT NEVER PAINTED, NEVER CONSUMED _?_
int init(){
IndicatorBuffers(4);
SetIndexBuffer( 0, G );SetIndexStyle( 0, DRAW_LINE, STYLE_SOLID, 1, Lime );
SetIndexBuffer( 1, R );SetIndexStyle( 1, DRAW_LINE, STYLE_SOLID, 1, Red );
SetIndexBuffer( 2, B3 );SetIndexStyle( 2, DRAW_NONE );
SetIndexBuffer( 3, B4 );SetIndexStyle( 3, DRAW_NONE );
return(0);
}
int start(){
if ( Bars <= 38 ) return(0); // JIT/RET in case Bars < 39 --^ --^ --^ --^
if ( T >= Bars ) T = Bars; // (TRIM´d) T < Bars .OR. = Bars
int aDrawBegins = Bars - T + P + 1; // ( extern P = 13 ) + 1 + ( Bars - ( extern T = 3000 if T < Bars else Bars ) )
//tIndexDrawBegin( 0, Bars - T + P + 1 ); // PREF: ( reused 4x )
SetIndexDrawBegin( 0, aDrawBegins ); // Draw 14+ last candles -- PREF: why a per tick hard-coded SHIFTING / enforced re-draw?
SetIndexDrawBegin( 1, aDrawBegins ); // Draw 14+ last candles -- PREF: why a per tick hard-coded SHIFTING / enforced re-draw?
SetIndexDrawBegin( 2, aDrawBegins ); // Draw 14+ last candles -- PREF: why a per tick hard-coded SHIFTING / enforced re-draw?
SetIndexDrawBegin( 3, aDrawBegins ); // Draw 14+ last candles -- PREF: why a per tick hard-coded SHIFTING / enforced re-draw?
double A, S1, S2; // auxiliary var for bar-mid-price calculi
int Z; // auxiliary stepper
int Opt = IndicatorCounted(); // Opt ( NEVER RE-USED )
if ( Opt < P ){ // if ( ( extern P = 13 ) > IndicatorCounted() )
// ----------------------- ??? ----------------------------------------------------- NEVER EXEC´d: for( Z = 1++ v/s Z <= 0 )
for ( Z = 1; Z <= 0; Z++ ) G[T-Z] = 0.0; // .STO G[T-Z], 0., BUT NEVER EXEC´d: for( Z = 1++ v/s Z <= 0 )
for ( Z = 1; Z <= 0; Z++ ) R[T-Z] = 0.0; // .STO R[T-Z], 0., BUT NEVER EXEC´d: for( Z = 1++ v/s Z <= 0 )
// ----------------------- ??? ----------------------------------------------------- NEVER EXEC´d: for( Z = 1++ v/s Z <= 0 )
}
Z = T - P - 1; // .STO Z, ( T = Bars (TRIM´d) ) - ( extern P = 13 ) - 1
while( Z >= 0 ){ // .DEC Z
// !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // PERF: very inefficient to RE-calc STATIC ( ( extern P = 13 ) - 1 )-DEEP CONVOLUTIONS per tick !!
S1 = 0.0; for ( int C = 0; C <= P - 1; C++ ){ S1 = S1 + ( High[Z+C] + Low[Z+C] ) / 2; }
S2 = 0.0; for ( int C = 0; C <= P - 1; C++ ){ S2 = S2 + ( ( High[Z+C] + Low[Z+C] ) * ( C+1 ) / 2 );}
// !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // PERF: very inefficient to RE-calc STATIC ( ( extern P = 13 ) - 1 )-DEEP CONVOLUTIONS per tick !!
A = S1 / S2;
G[Z] = A; // .STO G[Z], A if Z >= 0
if ( Z > 0 ){ R[Z-1] = A;} // .STO R[Z-1], A if Z > 0
Z--;
}
for ( int N = T - P - 2; N >= 0; N-- ){ // .STO N, ( T = Bars (TRIM´d) ) - ( extern P = 13 ) - 2
if ( N > 0 ){ // N > 0:
if ( G[N-1] > G[N] ){ R[N] = EMPTY_VALUE; continue;} // .BLNK R[N], EMPTY if G[N-1] > G[N]
if ( G[N-1] < G[N] ){ G[N] = R[N]; continue;} // .SET G[N], R[N] if G[N-1] < G[N]
}
// ?? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // WHY MANY-TIMES RE-ASSIGNED A CONST. VALUE HERE, INSIDE A FOR(){...}-loop body? -------------- ??
B3[0] = G[0] + P2; // .STO B3[0], G[0] + ( extern P2 = 0.001 )
B4[0] = G[0] - P2; // .STO B4[0], G[0] - ( extern P2 = 0.001 )
// forced quantise using 2 extra buffers
// ?? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // WHY MANY-TIMES RE-ASSIGNED A CONST. VALUE HERE, INSIDE A FOR(){...}-loop body? -------------- ??
}
return(0);
}
New-MQL4.56789 syntax
The OnCalculate() function is called only in custom indicators when it's necessary to calculate the indicator values by the Calculate event. This usually happens when a new tick is received for the symbol, for which the indicator is calculated. This indicator is not required to be attached to any price chart of this symbol.
The first rates_total parameter contains the number of bars, available to the indicator for calculation, and corresponds to the number of bars available in the chart.
We should note the connection between the return value of OnCalculate() and the second input parameter prev_calculated. During the OnCalculate() function call, the prev_calculated parameter contains a value returned by OnCalculate() during previous call. This allows for economical algorithms for calculating the custom indicator in order to avoid repeated calculations for those bars that haven't changed since the previous run of this function.
For this, it is usually enough to return the value of the rates_total parameter, which contains the number of bars in the current function call. If since the last call of OnCalculate() the price data has changed (a deeper history downloaded or history blanks filled), the value of the input parameter prev_calculated will be set to zero by the terminal.
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate( const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[]
)
{
// Get the number of bars available now for the current Symbol and chart period
int barsNow = Bars( _Symbol, PERIOD_CURRENT );
// .RET value of prev_calculated for a next call
return( rates_total );
}

Graph (Chart) Algorithm

Does anyone have a decent algorithm for calculating axis minima and maxima?
When creating a chart for a given set of data items, I'd like to be able to give the algorithm:
the maximum (y) value in the set
the minimum (y) value in the set
the number of tick marks to appear on the axis
an optional value that must appear as a tick (e.g. zero when showing +ve and -ve values)
The algorithm should return
the largest axis value
the smallest axis value (although that could be inferred from the largest, the interval size and the number of ticks)
the interval size
The ticks should be at a regular interval should be of a "reasonable" size (e.g. 1, 3, 5, possibly even 2.5, but not any more sig figs).
The presence of the optional value will skew this, but without that value the largest item should appear between the top two tick marks, the lowest value between the bottom two.
This is a language-agnostic question, but if there's a C#/.NET library around, that would be smashing ;)
OK, here's what I came up with for one of our applications. Note that it doesn't deal with the "optional value" scenario you mention, since our optional value is always 0, but it shouldn't be hard for you to modify.
Data is continually added to the series so we just keep the range of y values up to date by inspecting each data point as its added; this is very inexpensive and easy to keep track of. Equal minimum and maximum values are special cased: a spacing of 0 indicates that no markers should be drawn.
This solution isn't dissimilar to Andrew's suggestion above, except that it deals, in a slightly kludgy way with some arbitrary fractions of the exponent multiplier.
Lastly, this sample is in C#. Hope it helps.
private float GetYMarkerSpacing()
{
YValueRange range = m_ScrollableCanvas.
TimelineCanvas.DataModel.CurrentYRange;
if ( range.RealMinimum == range.RealMaximum )
{
return 0;
}
float absolute = Math.Max(
Math.Abs( range.RealMinimum ),
Math.Abs( range.RealMaximum ) ),
spacing = 0;
for ( int power = 0; power < 39; ++power )
{
float temp = ( float ) Math.Pow( 10, power );
if ( temp <= absolute )
{
spacing = temp;
}
else if ( temp / 2 <= absolute )
{
spacing = temp / 2;
break;
}
else if ( temp / 2.5 <= absolute )
{
spacing = temp / 2.5F;
break;
}
else if ( temp / 4 <= absolute )
{
spacing = temp / 4;
break;
}
else if ( temp / 5 <= absolute )
{
spacing = temp / 5;
break;
}
else
{
break;
}
}
return spacing;
}
I've been using the jQuery flot graph library. It's open source and does axis/tick generation quite well. I'd suggest looking at it's code and pinching some ideas from there.
I can recommend the following:
Set a visually appealing minimum number of major lines. This will depend on the nature of the data that you're presenting and the size of the plot you're doing, but 7 is a pretty good number
Choose the exponent and the multiplier based on a progression of 1, 2, 5, 10, etc. that will give you at least the minimum number of major lines. (ie. (max-min)/(scale x 10^exponent) >= minimum_tick_marks)
Find the minimum integer multiple of your exponent and multiplier that fits within your range. This will be the first major tick. The rest of the ticks are derived from this.
This was used for an application that allowed arbitrary scaling of data an seemed to work well.

Resources