2012年10月29日 星期一

Show Trade Price on AmiBroker

These AFLs reads external text files.
Show trade prices on the chart and also calculate equity change.
The trade file looks like this:
### Date Time, Current Position, Price ###
2012-09-25,-2,7771
2012-09-27,0,7700
2012-09-27,2,7700
2012-10-01,0,7689
2012-10-01,-2,7689
2012-10-02,0,7716
2012-10-02,2,7716
2012-10-04,-2,7700
2012-10-05,2,7715



  • izqStack.afl (put it in the Include folder, simple implementation of Stack)
/*

[20121016] zaqimon
Simple Stack implementation using VarGet/VarSet.
Store only number/array values.
Stack values are volatile over each PASS of AFL execution.
You will need StaticVar to persist Stack values.

*/

STACK_NAME = "DefaultStackName";


function SCountN(sname)
{
    out = VarGet(sname+"Count");
    if(IsNull(out))
        out = 0;
    return out;
}

function SPushN(sname,in)
{
    top = SCountN(sname);
    VarSet(sname+top, in);
    VarSet(sname+"Count", top+1);
}

function SPopN(sname)
{
    out = Null;
    top = SCountN(sname);
    if(top > 0)
    {
        top -= 1;
        VarSet(sname+"Count", top);
        out = VarGet(sname+top);
    }
    return out;
}

function SCount()
{
    return SCountN(STACK_NAME);
}

function SPush(in)
{
    SPushN(STACK_NAME,in);
}

function SPop()
{
    return SPopN(STACK_NAME);
}

  • ShowTrade.afl (drag-drop onto the price chart)
/*

[20121026] zaqimon
The trade file name is like SymbolName.txt
Place the trade file in TRADE_FILE_PATH directory
The trade file looks like this:
###  Date Time, Current Position, Price ###
2012-09-25,-2,7771
2012-09-27,0,7700
2012-09-27,2,7700
2012-10-01,0,7689
2012-10-01,-2,7689
2012-10-02,0,7716
2012-10-02,2,7716
2012-10-04,-2,7700
2012-10-05,2,7715
...

[20121018] zaqimon
* copy from BSShow.afl


*/

// initial options
EnableTextOutput(False); // disable output to commentary window
SetBarsRequired(sbrAll,sbrAll); // we need to see ALL bars
SetOption("FuturesMode",True); // need set FuturesMode True or PointValue will be 1 for good

// include
#include_once <izqStack.afl> // stack operations: SPush/SPop/SCount

// param
OverrideTradeFile = ParamStr("Override trade file",""); // empty string means using default Trade Signal Filename
ForceReload = ParamTrigger("Force reload", "!! CLICK to force reload !!");  // a force reload button

// Change these values first if needed
TRADE_FILE_PATH = "D:\\Futures\\Study\\ShowTrade\\"; // Trade Signal Filename, Name()+".txt"
TRADE_INTERVAL = inDaily; // match with Interval(2)
MAX_OPEN_POSITION = 999; // avoid unreasonable values
MAX_PPP = 5; // max pyramiding per day, 5 times per day should be far enough for trend trading
EQUITY_SYMBOL = "~zqTradeEquity"; // symbol name of calculated equity

// properties for PlotTradeSingle()
VarSet("COLOR_B", colorRed);
VarSet("COLOR_S", colorGreen);
VarSet("COLOR_T", colorBrightGreen);
VarSet("COLOR_R", colorDarkRed);
VarSet("ARROW_B", shapeUpArrow);
VarSet("ARROW_S", shapeHollowDownArrow);
VarSet("ARROW_T", shapeDownArrow);
VarSet("ARROW_R", shapeHollowUpArrow);

// default value for ~zqTradeEquity
gPLEquity = 0;
gPLPoint = 0;

function PushPrice(prevPos, nowPos, Price)
{
    for(i = abs(prevPos); i < abs(nowPos); i++) // should, abs(nowPos) > abs(prevPos)
    {
        SPush(Price*sign(nowPos)); // Push negative price on Short positions
    }
}

function PopPrice(prevPos, nowPos)
{
    out = 0;
    numLot = abs(prevPos) - abs(nowPos); // should, abs(nowPos) < abs(prevPos)
    for(i=0; i < numLot; i++)
    {
        out += SPop();
    }
    out /= numLot; // average entry price
    return out;
}

function SaveTradePrice(tradeType, Price, bi/*bar index*/)
{
    if(tradeType != "")
    {
        for(i=0;i<MAX_PPP;i++)
        {
            tp = StaticVarGet("TradePrice"+tradeType+i);
            if(typeof(tp)!="array" || IsNull(tp[bi])) // if available, aka. not used, save trade price
            {
                tp[bi] = Price;
                StaticVarSet("TradePrice"+tradeType+i,tp);
//_TRACE("SaveTradePrice: "+"TradePrice"+tradeType+i+"["+bi+"] = "+tp[bi]);
                break;
            }
        }
    }
    else
    {
        _TRACE("error, tradeType is empty");
    }
}

/*
 entry, exit prices are negative on Short trades for easy calculation
 lot is always positive for unambiguous
*/
function UpdateTradeEquity(entry, exit, lot/*how many lots offset*/, bi/*bar index*/)
{
    global gPLEquity, gPLPoint; // gPLEquity - equity P/L, gPLPoint - point P/L
    // P/L of this trade(offset)
    pt = exit*lot - entry*lot; // pt = (exit - entry) * lot // writing like this may incur rounding error. why? 12000 --> 11999.999
    eq = pt * PointValue;
    // cumulated P/L, typeof() is still 'number'
    pt += gPLPoint[bi];
    eq += gPLEquity[bi];
    // fill into global array with a little trick
    // we offset - pt, eq - nto array and fill them from [bi] to [BarCount-1]
    gPLPoint =  IIf(IsNull(Ref(pt, -bi)), gPLPoint,  Ref(pt,-bi));
    gPLEquity = IIf(IsNull(Ref(eq, -bi)), gPLEquity, Ref(eq,-bi));
}

/*
 save Trade Equiy to symbol - EQUITY_SYMBOL(~zqTradeEquity)
*/
function SaveTradeEquity()
{
    global gPLEquity, gPLPoint; // X = gPLEquity, Vol = gPLPoint
    
    if(typeof(gPLEquity)=="array" && typeof(gPLPoint)=="array")
    {
        AddToComposite(gPLEquity, EQUITY_SYMBOL, "X", atcFlagDeleteValues|atcFlagCompositeGroup|atcFlagEnableInIndicator);
        AddToComposite(gPLPoint,  EQUITY_SYMBOL, "V", atcFlagDeleteValues|atcFlagCompositeGroup|atcFlagEnableInIndicator);
    }
    else
    {
        AddToComposite(0, EQUITY_SYMBOL, "X", atcFlagDeleteValues|atcFlagCompositeGroup|atcFlagEnableInIndicator);
        AddToComposite(0, EQUITY_SYMBOL, "V", atcFlagDeleteValues|atcFlagCompositeGroup|atcFlagEnableInIndicator);
    }
}

/*
 on error, return <0; else, return lines processed correctly
 read file to StaticVar
 update ~zqTradeEquity
*/
function ReadTradeFile(tfname)
{
    out = 0;
_TRACE("ReadTradeFile: "+tfname);
    fh = fopen(tfname, "r");
    if(fh)
    {
        arDT = DateTime();
        cntDT = 0; // 1 pass index counter from 0 to BarCount-1
        prevPos = 0; // previous position
        avgEntryPrice = 0; // average entry price, also used as a trigger for equity calculation
        tradeType = ""; // "B","S","T","R" for Buy, Sell, shorT, coveR
        
        while(!feof(fh))
        {
            tpLine = fgets( fh );
            tpItem = 1 + StrCount(tpLine,",");
            if(tpItem != 3) // sanity check, 3 items expected
            {
                // don't care empty lines. bug?!
                // bug?! after the 2nd lines of multiple empty line, fgets return \n
                if(tpLine!="" && tpLine!="\n") _TRACE("error[ShowTrade]: tpItem count != 3 -- "+tpLine);
                continue;
            }
            tpDate = StrToDateTime(StrExtract(tpLine, 0)); // DateTime
            tpPos = StrToNum(StrExtract(tpLine, 1)); // current open positions, NaN will all be converted to 0
            tpPrice = StrToNum(StrExtract(tpLine, 2)); // trade price
            if(abs(tpPos) > MAX_OPEN_POSITION) // limit tpPos -999 ~ 999
            {
                _TRACE("error[ShowTrade]: tpPos exceed MAX_OPEN_POSITION -- "+tpLine);
                continue;
            }
            if(tpPrice<=0) // real trade price should always be positive
            {
                _TRACE("error[ShowTrade]: tpPrice should be positive !? -- "+tpLine);
                continue;
            }
            
            if(prevPos != tpPos) // positions changed
            {
                // cross 0, offset all positions before open new positions
                if(prevPos*tpPos < 0) // offset + open, crossing 0
                {
                    avgEntryPrice = PopPrice(prevPos, 0); // average entry price
                    offsetLot = abs(prevPos);
                    PushPrice(0, tpPos, tpPrice);
                    if(tpPos>0) // newly opened positions precede offset's, just ignore offset tradeType
                        tradeType = "B";
                    else
                        tradeType = "T";
                }
                else if(abs(tpPos)-abs(prevPos) < 0) // offset
                {
                    avgEntryPrice = PopPrice(prevPos, tpPos); // average entry price
                    offsetLot = abs(prevPos) - abs(tpPos);
                    if(prevPos>0)
                        tradeType = "S";
                    else
                        tradeType = "R";
                }
                else // open
                {
                    PushPrice(prevPos, tpPos, tpPrice);
                    if(tpPos>0)
                        tradeType = "B";
                    else
                        tradeType = "T";
                }
                
                // search bi (bar index) and save the trade price into StaticVar
                while(DateTimeDiff(arDT[cntDT], tpDate) < 0) // compare DateTime with DateTimeDiff(), NOT '<'
                {
                    if(cntDT >= BarCount-1) break; // bound check. from 0 ~ BarCount-1.
                    cntDT++;
                }
                if(arDT[cntDT] == tpDate) // date matched, save the trade price (positive price)
                {
                    SaveTradePrice(tradeType, tpPrice, cntDT); // for overlaying onto the price chart
                    // update ~zqTradeEquity, if there are offset trades, avgEntryPrice != 0 as a trigger
                    if(avgEntryPrice != 0)
                    {
                        UpdateTradeEquity(avgEntryPrice, tpPrice*sign(prevPos), offsetLot, cntDT); // price may be negative for easy calculation
                    }
                }
                avgEntryPrice = 0;
                prevPos = tpPos;
                ++out; // how many lines processed
            } // END if(prevPos != tpPos)
        } // END while(!feof(fh))
        SaveTradeEquity(); // save to symbol - "~zqTradeEquity"
        fclose(fh);
    }
    else
    {
        // fopen fail
        out = -1;
    }
    return out;
}

function PlotTradeSingle(type /* should be "B","S","T","R" */)
{
    for(i=0; i < MAX_PPP; i++)
    {
        arr_tp = StaticVarGet("TradePrice"+type+i);
        if(typeof(arr_tp) != "array")
            break;
        if(i == 0)
        {
            arrow_type = IIf(IsNull(arr_tp), shapeNone, VarGet("ARROW_"+type));
            arrow_color = IIf(type=="B" || type=="R", colorRed, colorBrightGreen); // VarGet("COLOR_"+type); // it looks weird if hollow arrows are darkened
            anchor_point = IIf(type=="B" || type=="R", L, H);
            // use default offset(-12), overlay solid and hollow arrows for cleanliness
            PlotShapes( arrow_type, arrow_color, 0 /*this is not Z-order*/ , anchor_point );
        }
        
        Plot(arr_tp, type+i, VarGet("COLOR_"+type), 
            styleDots | styleNoLine | styleNoTitle | styleNoLabel, 0, 0, 0, 10 /*Z-order*/ );
    }
}

function PlotTrades()
{
//    _TRACE("in PlotTrades");
    // Plot first show front. I'd like bright color at front.
    PlotTradeSingle("B"); // Buy - colorRed
    PlotTradeSingle("T"); // shorT - colorBrightGreen
    PlotTradeSingle("S"); // Sell
    PlotTradeSingle("R"); // coveR
}

function ShowTrade_Main()
{
    if(ForceReload)
    {
        _TRACE("!! Force Reloading !!");
        StaticVarRemove("STPrevSymbol");
    }
    // normal symbol && symbol changed && correct interval ==> ReadTradeFile()
    if(StrLeft(Name(),1) != "~" && 
    ( /* reload file when 1.change of OverrideTradeFile (if not empty) OR 2. change of Name() */
        (OverrideTradeFile != "" && OverrideTradeFile != StaticVarGetText("STPrevSymbol"))
        ||
        (OverrideTradeFile == "" && Name() != StaticVarGetText("STPrevSymbol"))
    )
    && Interval(0) == TRADE_INTERVAL)
    {
        // StaticVarRemove("*") here will re-read on each symbol change no matter ReadTradeFile successful or not
        StaticVarRemove("*"); // clear all StaticVar
        
        if(OverrideTradeFile != "")
        {
            StaticVarSetText("STPrevSymbol", OverrideTradeFile);
            TradeFile = TRADE_FILE_PATH + OverrideTradeFile;
        }
        else
        {
            StaticVarSetText("STPrevSymbol", Name());
            TradeFile = TRADE_FILE_PATH + Name() + ".txt";
        }
        re = ReadTradeFile(TradeFile);
        if(re >= 0)
        {
            StaticVarSetText("STLoadedSymbol", Name());
            // ~zqTradeEquity just get updated
            // we need little trick to make AmiBroker refresh again in order to show ~zqTradeEquity in ShowTradeEquity.afl
            AB = CreateObject("Broker.Application");
            AB.RefreshAll();
        }
    }
    
    // loaded symbol matched && correct interval ==> PlotTrades()
    if(Name() == StaticVarGetText("STLoadedSymbol") && Interval(0) == TRADE_INTERVAL)
    {
        PlotTrades();
    }
}

ShowTrade_Main();

//_TRACE(""+Name()+" : "+StaticVarGetText("STPrevSymbol")+" : "+StaticVarGetText("STLoadedSymbol"));

  • ShowTradeEquity.afl (insert new chart for displaying the calculated equity)
Ticker = ParamStr("Symbol", "~zqTradeEquity");
PlotForeign( Ticker, Ticker, ParamColor("Color", colorCycle ), ParamStyle("Style", styleArea, maskAll));

沒有留言:

張貼留言