Inherits From:
Declared In:

Class Description

The BFagent--"bitstring forecasting agent" is the centerpiece of the ASM model. The agent competes in a stock market, it buy, it sells. It decides to buy or sell by making predictions about what the price of the stock is likely to do in future. In order to make predictions, it keeps a large list of forecast objects on hand, and each forecast object makes a price prediction. These forecasts, which are created from the BFCast subclass, are fairly sophisticated entities, they may monitor many different conditions of the world. The forecast which has the best performance record at any given instant is used to predict the future price, which in turn leads to the buy/sell decision.

Inside the file BFagent.m, there is a long set of comments about the updating that went on in the redesign of this code for ASM-2.2. In order to faciliate this revision, several new classes were introduced. BFParams is an object that keeps values of the parameters for BFagents, and BFCast is the forecast object itself. BFCast, in turn, keeps its conditions bits with a subclass called BitVector.

If you dig into the code of this agent, you will find a confusing thing, so be warned. This code and articles based on it use the term "bit" to refer to something that can be valued either 0, 1, or 2. 0 means "don't care," 1 means "NO" and 2 means "YES". The confusing thing is that it takes two bits to represent this amount of information. In binary, the values would be {00,01,10}, respectively. I'm told some people call these trits to keep that in mind that two digits are required. As a result of the fact that it takes "two bits" to store "one bit's" worth of information, some relatively complicated book keeping has to be done. That's where all the parameters like "condbits" and "condwors" come into play. In ASM-2.0, that book keeping was all "manually done" right here in BFagent.m, but in the 2.2 version, it is all hidden in the subclass BitVector. So, for purposes of the interface of this class, a bit is a 3 valued piece of information, and values of bits inside forecasts are set by messages to the forecast, like [aForecast setConditionsbit: bit FromZeroTo: 2], for example, will set that bit to 2. If you want to know if a forecast has YES or NO for a bit x, [aForecast getConditionsbit: x].

Instance Variables

int currentTime;
int lastgatime;
double avspecificity;
double forecast;
double lforecast;
double global_mean;
double realDeviation;
double variance;
double pdcoeff;
double offset;
double divisor;
int gacount;
BFParams * privateParams;
id <Array> fcastList;
id <List> activeList;
id <List> oldActiveList;

currentTimeThe agent regularly checks with Swarm to see what time it is
lastgatimelast time period when the GeneticAlgorithm was run
avspecificityNo description.
forecastprediction of stock price: (trialprice+dividend)*pdcoeff + offset.
lforecastlagged forecast: forecast value from previous period
realDeviationftarget-lforecast: how far off was the agent's forecast?
variancean Exp.Weighted MA of the agent's historical variance: Combine the old variance with deviation^squared, as in: bv*variance + av*deviation*deviation
pdcoeffcoefficient used in predicting stock price, recalculated each period in prepareForTrading
offsetcoefficient used in predicting stock price, recalculated each period in prepareForTrading
divisora coefficient used to calculate demand for stock. It is a proportion (lambda) of forecastvar (basically, accuracy of forecasts)
gacounthow many times has the Genetic Algorithm been used?
privateParamsBFParams object holds parameters of this object
fcastListA Swarm Array, holding the forecasts that the agent might use
activeListA Swarm list containing a subset of all forecasts
oldActiveListA copy of the activeList from the previous time step

Method Types

+ setBFParameterObject:
+ init
- createEnd
- initForecasts
- createNewForecast
- setConditionsRandomly:
- prepareForTrading
- collectWorldData:
- updateActiveList:
- getInputValues
- feedForward
- getDemandAndSlope:forPrice:
- getRealForecast
- updatePerformance
- getDeviation
- updateWeights
- nbits
- nrules
- performGA
- lastgatime
- printcond:
- copyList:To:
- bitDistribution:cumulative:
- fMoments:cumulative:
- descriptionOfBit:

Class Methods


+ (void)init

This is vital to set values in the forecast class, BFCast, which in turn initializes BitVector class


+ (void)setBFParameterObject:x

This tells BFagents where they should look to get the default parameters. it should give the agent an object from the BFParams class.

Instance Methods


- (int)bitDistribution:(int *(*)[4])countptr cumulative:(BOOL)cum

Currently, this method is not called anywhere in ASM-2.2. It might serve some purpose, past or present, I don't know (pj: 2001-11-26)


- (BitVector *)collectWorldData:aZone

A forecast has a set of conditions it is watching. These are packed tight in a BitVector. We need the world data about the status of those conditions packed the same way, in order to make quick checks to find out if the world conditions are matched by the BitVector's conditions. This method creates a BitVector to match the conditions that are being monitored by the agent's forecasts. This requires the use of the design assumption that all of an agent's forecasts have the same bitlist.


- copyList:list To:outputList

This is a general utility method for Swarm lists. It removes all objects form the "outputList" and copies the elements from list into it. It does not actually destroy any elements from either list, it just updates references.


- createEnd

This creates the container objects activeList and oldActiveList. In addition, it makes sure that any initialization in the createEnd of the super class is done.


- (BFCast *)createNewForecast

Creates a new forecast object (instance of BFCast), with all condition bits set to 00 here, meaning "don't care. It also sets values for the other coefficients inside the BFCast. This method is accessed at several points throughout the BFagent class when new forecasts are needed.


- (const char *)descriptionOfBit:(int)bit

Currently, this method is not called anywhere in ASM-2.2. It might serve some purpose, past or present, I don't know (pj: 2001-10-26)


- (int)fMoments:(double *)moment cumulative:(BOOL)cum

Currently, this method is not called anywhere in ASM-2.2. It might serve some purpose, past or present, I don't know (pj: 2001-11-26)


- feedForward

Currently does nothing, used only if their are ANNagents


- (double)getDemandAndSlope:(double *)slope forPrice:(double)trialprice

Returns the agent's requested bid (if >0) or offer (if <0) using best (or mean) linear forecast chosen by -prepareForTrading. The forecast is given by

forecast = pdcoeff*(trialprice+dividend) + offset

where pdcoeff and offset are set by -prepareForTrading.

A risk aversion computation gives a target holding, and its derivative ("slope") with respect to price. The slope is calculated as the linear approximated response of a change in price on the traders' demand at time t, based on the change in the forecast according to the currently active linear rule.


- (double)getDeviation

Returns the absolute value of realDeviation


- getInputValues

Currently does nothing, used only if their are ANNagents


- (double)getRealForecast

Return agent's forecast


- initForecasts

initForecasts. Creates BFCast objects (forecasts) and puts them into an array called fCastList. These are the "meat" of this agent's functionality, as they are repeatedly updated, improved, and tested in the remainder of the class. Please note each BFagent has a copy of the default params object called privateParams. It can be used to set individualized values of settings in BFParams for each agent. That would allow true diversity! I don't see how that diversity would be allowed for in the ASM-2.0.


- (int)lastgatime

Return the last time the Genetic Algorithm was run. Agents that don't use a genetic algorithm return MININT. This may be used to see if the bit distribution might have changed, since a change can only occur through a genetic algorithm.


- (int)nbits

Returns the "condbits" variable from parameters: the number of condition bits that are monitored in the world, or 0 if condition bits aren't used.


- (int)nrules

Returns the number of forecasts that are used. In the original design, this was a constant set in the parameters, although revision of the code for ASM-2.2 conceivably should allow agents to alter the number of forecasts they maintain.


- performGA

Genetic algorithm. It relies on the following separate methods. (pj: 2001-11-25. I still see some room for improvement here, but the emphasis is to eliminate all global variables and explicitly pass return values instead. Any values needed for computations should either be passed explicitly or taken from someplace safe)

1. MakePool makes a list of the weakest forecasts: rejectList. That is the "npool" weakest rules.

2. "nnew" new rules are created. They are put into a Swarm list called newList. Their bit settings are taken from either crossover (using tournament selection to get parents), or mutation. "Tournament selection" means picking two candidates purely at random and then choosing the one with the higher strength. See the Crossover and Mutate methods for more details about how they work.

3. The nnew new rules replace weakest old ones found in step 1. This is done by the method "TransferFcastsFrom:To:" It pays no attention to strength, but looks at similarity of the bitstrings -- rather like tournament selection, we pick two candidates from the rejectList at random and choose the one with the MORE similar bitstring to be replaced. This maintains more diversity.

4. Generalize looks for rules that haven't been triggered for "longtime" and generalizes them by changing a randomly chosen fraction "genfrac" of 0/1 bits to "don't care". It does this independently of strength to all rules in the population.

There are several private methods that take care of this work. They don't show up in the public interface, but here they are:

-(BFCast *) CopyRule:(BFCast *) to From: (BFCast *) from

-(void) MakePool: rejects From: (id <Array>) list

-(BOOL) Mutate: (BFCast *) new Status: (BOOL) changed

-(BFCast *) Crossover:(BFCast *) newForecast Parent1: (BFCast *) parent1 Parent2: (BFCast *) parent2

- (void) TransferFcastsFrom: newlist To: forecastList Replace: rejects

- (BFCast *) GetMort: (BFCast *) new Rejects: (id <List>) rejects

-(void) Generalize: (id) list AvgStrength: (double) avgstrength

Parameter list:

npool-- size of pool of weakest rules for possible relacement; specified as a fraction of numfcasts by "poolfrac"

nnew-- number of new rules produced specified as a fraction of numfcasts by "newfrac"

pcrossover-- probability of running Crossover.

plinear-- linear combination "crossover" prob.

prandom-- random from each parent crossover prob.

pmutation-- per bit mutation prob.

plong-- long jump prob.

pshort-- short (neighborhood) jump prob.

nhood-- size of neighborhood.

longtime-- generalize if rule unused for this length of time

genfrac-- fraction of 0/1 bits to make don't-care when generalising


- prepareForTrading

Set up a new active list for this agent's forecasts, and compute the coefficients pdcoeff and offset in the equation forecast = pdcoeff*(trialprice+dividend) + offset

The active list of all the fcasts matching the present conditions is saved for later updates.


- printcond:(int)word

in case you want to see the 0101 representation of an integer. Sometimes this comes in handy if you are looking at a particular forecast's value as an int and you need to convert it to the 0's and 1's


- setConditionsRandomly:(BFCast *)fcastObject

Take a forecast object and randomly change the bits that govern which conditions it monitors. This appears to be a piece of functionality that could move to the BFCast class itself. There were quite a few of these details floating around in BFagent at one time, many are gone now.


- updateActiveList:(BitVector *)worldvalues

This is the main inner loop over forecasts. Go through the list of active forecasts, compare how they did against the world. Notice the switch that checks to see how big the bitvector (condwords) is before proceeding. At one time, this gave a significant speedup. The original sfsm authors say 'Its ugly, but it works. Don't mess with it!' (pj: I've messed with it, and don't notice much of a speed effect on modern computers with modern compilers :> My alternative implementation is commented out inside this method)


- updatePerformance

Now update the variance and strength of all the forecasts that were active in the previous period, since now we know how they performed. This method causes an update of price/dividend information from the world, then it measures how far off each forecast was and puts the square of that "deviance" measure into the forecast with the forecast's setVariance: method. Each forecast in the active list is told to update its forecast. It also updates the instance variable variance, which is calculated here as an exponentially weignted moving average of that forecast's squared-error (variance). Inside the code of updatePerformance, there is a description of the strength formula that is used, and how the formula now matches the formula used in the original sfsm, rather than ASM-2.0.


- updateWeights

Currently, does nothing, used only if their are ANNagents

Version 1.1 Copyright ©2001. All Rights Reserved.