No BSD License  

Highlights from
A baby trading system

image thumbnail
from A baby trading system by Dimitri Shvorob
(No, we don't trade babies!)

tradebot
function tradebot
% TRADEBOT  A naive automated trader
% Example : tradebot

global fileNames  ...
       fileFormats ...
       colors ...
       universeList   ...
       universeSize   ...
       portValue      ...
       epochsTrain    ...
       epochTimer     ...
       epochCount

fileNames   = {'universelist.txt', ...
               'universeprices.txt', ...
               'universereturns.txt', ...
               'portfolioreturns.txt', ...
               'traderecords.txt'};
fileFormats = {'%d', ...
               '%d\t%d\t%f', ...
               '%d\t%d\t%f', ...
               '%d\t%f\t%f\t%f', ...
               '%d\t%d\t%f'};
     
colors = {[.8 .8 .8], ...  % gray
          [.6 .2 .3], ...  % red   
          [.2 .6 .3]};     % green  
     
[universeList] = textread(fileNames{1},fileFormats{1});   % arbitrary numeric ids
universeSize   = length(universeList);

portValue   = 100;
    
epochsTrain = 3; 
epochsTrade = 30;
epochLength = 2;
epochCount  = 0;
epochTimer  = timer('StartDelay',0, ...
                    'Period',epochLength, ...
                    'TasksToExecute',epochsTrain + epochsTrade, ...
                    'ExecutionMode','fixedRate',  ...
                    'TimerFcn',@completeEpoch,    ...
                    'StopFcn', @abortStrategy);
createGUI               
end

function createGUI
global hList  ...
       hPlot  ...
       hCash  ...
       hTime  ...
       hStop  ...
       portValue ...
       colors 
f = figure('Name','Tradebot: (Not Quite) Ready For Wall Street', ...
           'Position',[3 500 400 270], ...
           'NumberTitle','off', ...
           'Color','w', ...
           'Resize','off', ...
           'MenuBar','none');
x = [.40 .68];
y = [.05 .15];
w = .25;
v = .10;
hStrt = guicontrol('pushbutton',f,[x(1) y(1) w v],'Start'); 
hStop = guicontrol('pushbutton',f,[x(2) y(1) w v],'Stop');  
hTime = guicontrol('text'      ,f,[x(1) y(2) w v],'Epoch: 0');
hCash = guicontrol('text'      ,f,[x(2) y(2) w v],sprintf('Value: %.2f',portValue));
hList = addPortfolioListPanel;                   %#ok
hPlot = addPortfolioReturnPanel;                 %#ok
set(hStrt,'Callback',@startStrategy)
set(hStop,'Callback',@abortStrategy)

    function[h] = addPortfolioListPanel
    h = nan(10,2);
    w = [.13 .13];
    x = [.03 .17];
    v = .066;   
    for i = 1:12
        y = .90 - (i-1)*(v + .01);
        if i == 1
           set(guicontrol('text',f,[x(1) y .27  v],'Current portfolio'),'Fontweight','bold')
        elseif i == 2
           set(guicontrol('text',f,[x(1) y w(1) v],'ID'   ),'BackgroundColor',colors{1})
           set(guicontrol('text',f,[x(2) y w(2) v],'Weight'),'BackgroundColor',colors{1})
        else
           h(i-2,:) = [guicontrol('text',f,[x(1) y - 0.01 w(1) v],'') ...
                       guicontrol('text',f,[x(2) y        w(2) v],'')];
        end
    end 
    end

    function[h] = addPortfolioReturnPanel
    x = .40;
    w = .53; 
    v = .50;
    y = .87 - v;
    set(guicontrol('text',f,[x .90 w .06],'Performance'),'Fontweight','bold')
    h = axes('Parent',f, ...
             'Position',[x y w v], ...
             'FontSize',8, ...
             'Box','on');
    end

end

function completeEpoch(obj,event)                 
global hList  ...
       hPlot  ...
       hCash  ...
       hTime  ...
       fileNames   ...
       fileFormats ...
       colors ...
       universeList ...
       universeSize ...
       portValue    ...
       portWeights  ...
       epochsTrain  ...
       epochCount
if portValue > 0 
   epochCount = epochCount + 1; set(hTime,'String',sprintf('Epoch: %d',epochCount))
   updateUniverseData
   if epochCount == epochsTrain
      reviewPortfolioWeights
   elseif epochCount > epochsTrain
      computePortfolioReturn
      reviewPortfolioWeights
   end   
else
   abortStrategy(obj,event)
end    

    function updateUniverseData
    global currentUniverseReturns
    persistent previousUniversePrices
    
    currentUniversePrices = 100 + .1*randn(universeSize,1);
    if epochCount > 1
       currentUniverseReturns = currentUniversePrices./previousUniversePrices - 1;
    else
       currentUniverseReturns = nan*currentUniversePrices;
    end   
    
    f = fopen(fileNames{2},'at');
    for i = 1:universeSize
        fprintf(f,[fileFormats{2} '\n'],epochCount,universeList(i),currentUniversePrices(i));
    end
    fclose(f);
    
    f = fopen(fileNames{3},'at');
    for i = 1:universeSize
        fprintf(f,[fileFormats{3} '\n'],epochCount,universeList(i),currentUniverseReturns(i));
    end
    fclose(f);
    
    previousUniversePrices = currentUniversePrices;
    end

    function reviewPortfolioWeights
    global portRetExp
    [epoch,id,ret] = textread(fileNames{3},fileFormats{3});
    
    movAvgReturn = nan(universeSize,1);
    test1 = epoch >= epochCount - 5;
    for i = 1:universeSize                       %#ok
        test2 = id == universeList(i);
        movAvgReturn(i) = nanmean(ret(test1 & test2));
    end  
    [y,movAvgReturnRank] = sort(movAvgReturn);
    
    goLong  = movAvgReturnRank > (universeSize - 5);
    goShort = movAvgReturnRank <= 5;
    
    candWeights = zeros(universeSize,1);
    candWeights(goLong)  =  1/sum(goLong);
    candWeights(goShort) = -1/sum(goShort);
    if epochCount == epochsTrain
       portWeights = candWeights;
    end
    
    er0 = portWeights'*movAvgReturn;
    er1 = candWeights'*movAvgReturn;
    if er1 > er0
       portWeights = candWeights;
       portRetExp  = er1;
    else
       portRetExp  = er0;
    end

    recordTrade
    updateDisplay
    
        function updateDisplay
        j = 1;
        for i = 1:universeSize                   %#ok
            if portWeights(i) ~= 0
               set(hList(j,1),'String',sprintf('%07d',universeList(i)))
               set(hList(j,2),'String',sprintf('%+.2f',portWeights(i)), ...
                              'BackgroundColor',colors{2+sign(portWeights(i))})
               j = j + 1;
            end   
        end
        end
        
        function recordTrade
        f = fopen(fileNames{5},'at');
        for i = 1:universeSize                   %#ok
            fprintf(f,[fileFormats{5} '\n'],epochCount,universeList(i),portWeights(i));
        end
        fclose(f);
        end

    end

    function computePortfolioReturn
    global currentUniverseReturns ...
           portRetExp
    portReturn = portWeights'*currentUniverseReturns;
    portValue  = portValue*(1 + portReturn);
    f = fopen(fileNames{4},'at');
    fprintf(f,[fileFormats{4} '\n'],epochCount,portReturn,portRetExp,portValue);
    fclose(f);
    updateDisplay
           
        function updateDisplay
        [epoch,ret,retexp,x] = textread(fileNames{4},fileFormats{4});  %#ok
        w = 20;    % size of moving window
        n = length(epoch);
        t = w*floor((n - 1)/w) + 1;
        u = t + w - 1;
        i = t:n;
        h = plot(hPlot,i,ret(i),i,retexp(i));
        set(h(1),'LineWidth',2,'Color',colors{2})
        set(h(2),'LineWidth',2,'Color',colors{1})
        set(legend('Realized return','Forecast return'),'Box','off')
        set(hCash,'String',sprintf('Value: %.2f',portValue))
        set(hPlot,'XLim',[t u])
        end
        
    end

end

function startStrategy(obj,event)                %#ok
global fileNames  ...
       colors ...
       epochTimer
set(obj,'Callback','','ForegroundColor',colors{1})
for i = 2:5
    if exist(fileNames{i},'file')
       delete(fileNames{i})
    end 
end  
start(epochTimer)   
end    
    
function abortStrategy(obj,event)               %#ok
global hStop  ...
       colors ...
       epochTimer
set(hStop,'Callback','','ForegroundColor',colors{1})
stop(epochTimer)
delete(epochTimer)
end

function[varargout] = guicontrol(style,parent,pos,string)
h = uicontrol(...
    'Parent',parent, ...
    'Units','normalized', ... 
    'Position',pos, ...
    'Style',style, ...
    'String',string, ...
    'FontSize',9,...
    'HorizontalAlignment','center', ...
    'BackgroundColor','w'); 
if nargout > 0
   varargout(1) = {h};
end
end

Contact us at files@mathworks.com