將文字檔內的交易價位標於K線圖上
本帖最後由 zaqimon 於 12-10-29 12:15 編輯不知道有沒有人分享過
不過我也懶得找了所以自己寫一個
這三個AFL能把文字檔內紀錄的交易價位標示於K線圖上
並計算出損益Equity
也許可以當作回顧歷史交易的用途吧
有需要請自行修改
文字檔放置路徑(TRADE_FILE_PATH)請自行設定
文字檔預設檔名為symbol_name.txt
文字檔內容如下
###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 (請放到Include資料夾內)
/*
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 (請拖拉到主要K線圖上)
/*
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
...
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)) // if available, aka. not used, save trade price
{
tp = Price;
StaticVarSet("TradePrice"+tradeType+i,tp);
//_TRACE("SaveTradePrice: "+"TradePrice"+tradeType+i+"["+bi+"] = "+tp);
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;
eq += gPLEquity;
// fill into global array with a little trick
// we offset - pt, eq - into array and fill them from to
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: 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: tpPos exceed MAX_OPEN_POSITION -- "+tpLine);
continue;
}
if(tpPrice<=0) // real trade price should always be positive
{
_TRACE("error: 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, tpDate) < 0) // compare DateTime with DateTimeDiff(), NOT '<'
{
if(cntDT >= BarCount-1) break; // bound check. from 0 ~ BarCount-1.
cntDT++;
}
if(arDT == 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 (顯示Equity用)
Ticker = ParamStr("Symbol", "~zqTradeEquity");
PlotForeign( Ticker, Ticker, ParamColor("Color", colorCycle ), ParamStyle("Style", styleArea, maskAll));
板大認真唷~~~
@zaqimon,, thanks for sharing
Have you tried
SetOption( "RefreshWhenCompleted" );
instead of
AB = CreateObject( "Broker.Application" );
AB.RefreshAll();
@zaqimon,
Does intraday datetime work also?
like
###Date Time, Current Position, Price ###
2012-09-25 07:01,-2,7771
2012-09-27 09:45,0,7700
2012-09-27 13:23,2,7700
2012-10-01 08:56,0,7689
2012-10-01 16:43,-2,7689
Is -2 -> short
2 -> buy?
0 -> ? 多謝版主分享{:7_420:} joshsmi 發表於 12-10-30 06:44 static/image/common/back.gif
@zaqimon,
Does intraday datetime work also?
我猜intraday應該也可以用吧
然後TRADE_INTERVAL改成例如in1Minute之類的
不過我沒測試過
0就是手上沒有倉位(純平倉用)
2就是多單部位持倉兩口
-4就是空單部位持倉四口
文字檔紀錄可以直接多空對翻不需要紀錄平倉
程式會自動先平倉計算損益然後再開新倉
例如可以這樣寫表示
先進空單三口
再進多單八口(內含平倉的三口)也就是變成多單持倉五口
2012-09-25,-3,7771
2012-09-27,5,7700
感謝版大無私的分享
先收下 好好研究一番
頁:
[1]