📄 emhftrader.m
字号:
// EMHFTrader.m// This file defines the method of stock valuation for// fundamental traders who believe the markets are efficient....// An implication of this belief is that they expect the // price to be correct in the coming period and all that follow.// Hence, they derive their expected return by calculating the// fundamental value and the expected fundamental value in the// future.//#import "EMHFTrader.h"// Implementation of a EMH fundamental trader ..@implementation EMHFTrader-createEnd { // ensure all distributions are initialized [super createEnd]; // This is a quarterly rate, can override 2.5% default from Trader // discountRate = 0.10; // let them start out well expRiskyCFLag = [mktStats getEarningsLag: 0]; // for use in determining demand schedule each period dmdList = [List create: [self getZone]]; supList = [List create: [self getZone]]; [self projectEarnings];return self;}-printEstimates { if (diagLevel & TRADER_STRATEGY) { printf ("<%s> in (%d) days, expEarnings (%f), priorEarnings (%f) \n", [self getName], daysTillEarnings, risky1CF, expRiskyCFLag ); } return self;}-projectEarnings { double rho; if (diagLevel & METHOD_ENTRY) printf("*** projectEarnings (EMHFTrader) \n"); // Determine expected 1-period ahead price and periodic cash flow // Use rational expectations forecast. // If CF follows ARMA(1,1) process as described in paper, // rational expectations forecast of next CF is weighted // avg of last CF and last forecast CF rho = 1+riskyGrowthRate; riskyCF = [mktStats getEarningsLag:0]; // for initial forecast if (expRiskyCFLag == 0) expRiskyCFLag = riskyCF; correctCFEst = 0.75 * expRiskyCFLag + (rho - 0.75) * riskyCF; // for those who estimate with a bias risky1CF = correctCFEst + (correctCFEst*[self getBias]); // save forecast for next time expRiskyCFLag = risky1CF; if (diagLevel & TRADER_STRATEGY) [self printEstimates];return self;}-(double) getBias { return (0);}-updateProjections { if (diagLevel & TRADER_STRATEGY) [self printEstimates]; // This trader does not update projections based on bogus information. return self;}-printValue { if (diagLevel & TRADER_STRATEGY) printf (" values risky at (%f) and P1 (%f) with (%d) til earnings.\n", riskyValue, expP1, daysTillEarnings); return self;}-calcSchedule { double dmdTotal; id index; ShareRequest * aRequest;#if 0 [self calcShrsDemand: (riskyValue)*(1-0.15)]; [self calcShrsDemand: (riskyValue)*(1-0.10)]; [self calcShrsDemand: (riskyValue)*(1-0.050)];#endif [self calcShrsDemand: (riskyValue)*(1-0.020)]; [self calcShrsDemand: (riskyValue)*(1-0.010)]; [self calcShrsDemand: (riskyValue)*(1-0.005)]; [self calcShrsDemand: (riskyValue)*(1+0.005)]; [self calcShrsDemand: (riskyValue)*(1+0.010)]; [self calcShrsDemand: (riskyValue)*(1+0.020)];#if 0 [self calcShrsDemand: (riskyValue)*(1+0.050)]; [self calcShrsDemand: (riskyValue)*(1+0.10)]; [self calcShrsDemand: (riskyValue)*(1+0.150)];#endif // Calculate Demand @ perceived value last so probed values // are more representative. [self calcShrsDemand: (riskyValue)*(1-0.00)]; // Go through demand/supply lists and adjust from gross amount of // shares demanded at the price point to net shares demanded at // the price point. For bids, this accounts for the fact as the // price falls (when clearing), greater bids will already be filled. dmdTotal = 0; index = [(id) dmdList begin: scratchZone]; while (( aRequest = [index next])) { [aRequest setNetUnits: [aRequest getGrossUnits] - dmdTotal]; dmdTotal = [aRequest getGrossUnits]; } [index drop]; dmdTotal = 0; index = [(id) supList begin: scratchZone]; while (( aRequest = [index next])) { [aRequest setNetUnits: [aRequest getGrossUnits] - dmdTotal]; dmdTotal = [aRequest getGrossUnits]; } [index drop]; // Submit demand schedule to the mkt maker index = [(id) dmdList begin: scratchZone]; while (( aRequest = [index next])) { [mktMaker addRiskyDemand: aRequest]; } [index drop]; index = [(id) supList begin: scratchZone]; while (( aRequest = [index next])) { // We've been working with neg #'s for supply since this // is a number net of what they've currently got. Need // to change to pos # so MktMaker can handle correctly [aRequest setNetUnits: (-[aRequest getNetUnits])]; [aRequest setGrossUnits: (-[aRequest getGrossUnits])]; [mktMaker addRiskySupply: aRequest]; } [index drop]; // Clean lists. Market maker should delete share request objects // when he's finished clearing the market [dmdList removeAll]; [supList removeAll]; return self;}-calcShrsDemand: (double) p0 { void addAscend(), addDescend(); double expRet; double grossUnits; ShareRequest * aRequest; if (daysTillEarnings == 1) { expRet= (expP1 - p0 + risky1CF) / p0; } else { expRet= (expP1 - p0) / p0; } expRiskyRet = expRet; optRiskyFraction = (expRiskyRet- riskLessRate) / (riskAversion * rvar); // Calculate units to buy/sell at this price optRiskyUnits = (wealth * optRiskyFraction) / p0; // gross order is amount of demand net of current holdings grossUnits = optRiskyUnits - riskyUnits; // if gross > 0, add to demand list (will bid for shares) // if gross < 0, add to supply list (will offer shares) // if gross = 0, ignore this price point aRequest = [ShareRequest create: [self getZone]]; [aRequest setUnitPrice: p0]; [aRequest setGrossUnits: grossUnits]; [aRequest setTrader: (id *) self]; if ([aRequest getGrossUnits] > 0) addAscend( (id) dmdList, (id) aRequest); else if ([aRequest getGrossUnits] < 0) addDescend( (id) supList, (id) aRequest); return self;}-printPortChoice { if (diagLevel & TRADER_STRATEGY) {#if 0 printf(" Portfolio Choice (BID) r (%f) var (%f) frac (%f) units (%f)\n", expRetBid, rvar, optRiskyFractionBid, optRiskyUnitsBid); printf(" Portfolio Choice (OFFER) r (%f) var (%f) frac (%f) units (%f)\n", expRetOffer, rvar, optRiskyFractionSell, optRiskyUnitsSell);#endif } return self;}-step { ShareRequest * aRequest; double dlyRate, pvif, t1, t2a, t2; dlyRate = t2 = 0; if ((diagLevel & METHOD_ENTRY) || (diagLevel & METHOD_SCHED)) printf("*** step (EMHFTrader) entry\n"); [super step]; // BANKRUPTCY CODE: IF WEALTH IS NEGATIVE, THE TRADER MUST // CLEAR ALL POSITIONS IN THE RISKY ASSET (LONG OR SHORT) // Note: If the trader already has non-positive wealth, the // MktMaker has issued mkt orders on their behalf. The only // things they can do is check liquidity results and exit. They // will not be able to trade until they have sufficient wealth // through riskless growth or positive liquidity. if ([self getWealth] < 0) { liqNeeds = [self getLiqNeeds]; wealth = [self getWealth]; [self printWealth]; return self; } else { liqNeeds = [self getLiqNeeds]; wealth = [self getWealth]; [self printWealth]; } // fundamental strategy is to buy or sell at the // fundamental price. We buy or sell based on // portfolio re-balancing needs which arise from // liquidity needs and investment gains // if (daysTillEarnings == 0) { [self projectEarnings]; // Use Gordon Model for now // NOTE: all terms are in qtly terms riskyValue = risky1CF / (discountRate - riskyGrowthRate); } else { // must discount expected earnings and expected // price based upon gordon growth model // some traders change CF projections between earnings // annonuncements. [self updateProjections]; dlyRate = discountRate / 90; pvif = pow ((double)1+dlyRate, (double)daysTillEarnings); t1 = risky1CF / pvif; t2a = (risky1CF * (1+riskyGrowthRate))/ (discountRate - riskyGrowthRate); t2 = t2a / pvif; riskyValue = t1 + t2; } // derive "expected" clearing price in one period based on today's // expected clearing price. Special case is when dividend is // distributed tomorrow. Then price will drop, but present // value of price component of today's price will appreciate if (daysTillEarnings == 1) expP1 = t2 * (1+dlyRate); else expP1 = riskyValue * (1+dlyRate); [self printValue]; // Value must be positive. // If value is not positive, try to unload all shares // of the risky asset at whatever the market will pay, // if we were short the risky asset, then attempt to // close out our position at whatever market will pay // if (riskyValue <= 0) { riskyValue = -1; optRiskyUnits = 0; aRequest = [ShareRequest create: [self getZone]]; [aRequest setUnitPrice: -1]; [aRequest setGrossUnits: 0]; [aRequest setNetUnits: riskyUnits]; [aRequest setTrader: (id *) self]; [mktMaker addRiskySupply: aRequest]; } else { // Fundamental trader will choose a strategy // based on what they value the risky asset // at. The number of shares demanded at prices // above and below this value is a function of // the return variance and the trader's risk // aversion. rvar = [mktStats getRiskyRetVar]; // calculate my demand schedule [self calcSchedule]; } // end else value is positive/* [self printPortChoice]; [self printDemand];*/ return self;}-print { printf("FUND TTEST \n"); return self;}void addAscend( id list, ShareRequest *s) { ShareRequest *aRequest; id index; if ([list getCount] != 0 ) { index = [(id) list begin: scratchZone]; while ((aRequest = [index next])) { // if <= then add in front if ([s getGrossUnits] <= [aRequest getGrossUnits]) { [index addBefore: s]; break; } } [index drop]; // if we got to the end of the list, index will be // nil, but the trader won't be on teh list if (aRequest == nil) [list addLast: s]; } else // this is the first [list addLast: s];}void addDescend( id list, ShareRequest *s) { ShareRequest *aRequest; id index; if ([list getCount] != 0 ) { index = [(id) list begin: scratchZone]; while ((aRequest = [index next])) { // if >= then add in front if ([s getGrossUnits] >= [aRequest getGrossUnits]) { [index addBefore: s]; break; } } [index drop]; // if we got to the end of the list, index will be // nil, but the trader won't be on teh list if (aRequest == nil) [list addLast: s]; } else // this is the first [list addLast: s];}@end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -