Introduction to the ATC

The AddToComposite() (ATC) is an extremely powerful function. Topics in this category will not repeat ATC topics covered elsewhere, so be sure to follow the following links for more information and applications:

The AmiBroker AFL Reference: ADDTOCOMPOSITE
Calculating multiple-security statistics with AddToComposite function
Introduction to AddToComposite
Multiple time-frame indicators
Search this users’ Knowledge Base for applications

Also be sure to browse the applications listed at the bottom of the AFL Reference for the ATC.

1 Star2 Stars3 Stars4 Stars5 Stars (2 votes, average: 5 out of 5)
Loading ... Loading ...

Real-Time Bar-Period Timing

In real-time trading one often needs to know when a new period starts and how much time there is left before the period ends. The code below provides this information. Be sure to synchronize your system’s clock.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function GetSecondNum() 
{ 
Time = Now(4); 
Seconds = int(Time%100); 
Minutes = int(Time/100%100); 
Hours = int(Time/10000%100); 
SecondNum= int(Hours*60*60+Minutes*60+Seconds); 
return SecondNum; 
} 
 
RequestTimedRefresh(1); 
TimeFrame = Interval(); 
SecNumber = GetSecondNum(); 
Newperiod = SecNumber%TimeFrame == 0; 
SecsLeft = SecNumber-int(SecNumber/TimeFrame)*TimeFrame; 
SecsToGo = TimeFrame - SecsLeft; 
if( NewPeriod )  
{ 
Say("New period"); 
Plot(1,"",colorYellow,styleArea|styleOwnScale,0,1); 
} 
Title = "\n"+ 
"time: "+Now(2)+"\n"+ 
"Interval: "+NumToStr(TimeFrame,1.0)+"\n"+ 
"Second Number: "+NumToStr(SecNumber,1.0,False)+"\n"+ 
"Seconds Left: "+NumToStr(SecsLeft,1.0,False)+"\n"+ 
"Seconds To Go: "+NumToStr(SecsToGo,1.0,False);

For testing and code verification timing is displayed in the chart title:

clip_image002

1 Star2 Stars3 Stars4 Stars5 Stars (5 votes, average: 4.4 out of 5)
Loading ... Loading ...

High-Frequency Automated Trading (HFAT); part 2

Interactive Brokers’ Real-Time Volume Data

Just like with price data, volume data are subject to delays and BF (Backfill) corrections. Moreover, IB (Interactive Brokers) reports volume data in a manner that could cause major performance differences between backtesting and actual trading.

This post outlines simple procedures to collect RT and BF data for comparison. No effort is made to explain the differences or to perform statistical analysis. The views expressed here are based on personal experiences and/or may be anecdotal; not everything that happens in real-time trading is easy to explain. As always, if you have technical insight and/or see inaccuracies, please comment for the benefit of future readers.

As expected, IB RT volume data contain the usual bad ticks and delays that are corrected during backfill. However, and this is very important to the RT trader, IB adjusts live volumes at about 30-second intervals. This means that the volumes IB reports during RT trading do not accurately reflect market activity. This means also that volume data may be delayed by up to 30 seconds, instead of by the typical snapshot delay, which is about 300 milliseconds for price data. Comparing backfilled with real-time volume, it appears that the real-time periodic volume adjustments are re-distributed across individual snapshots during backfill. This post is intended to help you perform your own data analysis. The methods outlined below are intended to get you started.

To collect and save real-time data:

  1. Create a new database in the 5-second interval.
  2. Embed “RD”, for Raw Data, when naming the database.
  3. In Database Settings select the Interactive Brokers plugin.
  4. Pick a high volume stock, for example, AAPL (used in this post).
  5. Connect to the TWS (Trader Work Station), signing in to your Paper Trading account. Do not use the eDemo account.
  6. Collect about an hour’s worth of real-time data.

The first thing that will happen when you connect to the TWS is that AmiBroker backfills approximately 2000 bars of 5-second data. This cannot be prevented and you must be careful to note the time where backfill ends and raw data collection starts. The simplest way is to place a vertical line on your chart and label it “Start of real-time data”.

To save the database:

  1. Disconnect the IB plugin (see Plugin menu at right bottom of chart).
  2. Open Database Settings and set the database to Local.
  3. Place another vertical line to indicate where data collection stopped.
  4. Go to the File menu and save the database.

Be sure to set the Database Settings -> Data Source -> Local before saving. If you do not do this the database will backfill on the next startup and this may corrupt your RT data sample.

The next step is to collect a sample of BF data that overlaps the previously collected real-time sample. To do this, you need to create another database. Since IB backfills only about 2000 bars of 5-second data, you should do this as soon as possible after collecting raw data, else the collection periods may not overlap and you will not be able to compare the two types of data. The procedure is the same as above except that you want to embed “BF” (for backfilled data) instead of “RD” in the database name.

To visually compare the two databases you can open two instances of AmiBroker and load the RT database in one and the BF database in the other. You can then display the two databases at the same time and visually compare the respective charts. You may want to display both a price chart and a volume chart in separate panes, as shown in the captures below.

You can use the code below to inspect your price chart:

1
2
3
4
5
6
Plot(C,"Close",colorBlack,styleBar); 
TN=TimeNum(); 
Cursortime = SelectedValue(TN); 
CumHL = Cum(IIf(TN>=CursorTime,H-L,0)); 
Plot(CumHL,"",4,styleArea|styleOwnScale); 
Title=Name()+" Interactive Brokers BackFilled price data - "+Interval(2);

And this code to inspect your Volume chart:

1
2
3
4
5
6
Plot( Volume,"",2,styleOwnScale|styleHistogram|styleThick); 
TN=TimeNum(); 
Cursortime = SelectedValue(TN); 
CV = Cum(IIf(TN>=CursorTime,V,0)); 
Plot(CV,"",4,styleArea); 
Title="Backfilled Volume data - "+Interval(2);

The above formulas will display basic charts plus a cumulative value (red area) for any parameter you would like to test. In the price chart, high-low range (H-L) is summed while in the Volume chart plain Volume is summed. Summation starts with the cursor-selected bar. This feature is only provided to visually reveal data differences; it has no other significance.

The charts below were created using the above methods, which quickly reveal the difference between the two types of data. To explain why these difference occur is left up to the expert reader (because I don’t have a clue!!).

clip_image002[16]

Figure 1 - Backfilled data

clip_image004[16]

Figure 2 - Real-Time Collected data

The following volume indicator can be used to display the RT volume periodicity more clearly:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Filename = StrLeft(_DEFAULT_NAME(),StrLen(_DEFAULT_NAME())-2); 
Vref = Ref(HHV(V,4),-1); 
VSpike = V > Vref AND V>Ref(VRef,-1)/2; 
BS=ValueWhen(VSpike,BarsSince(Ref(VSpike,-1))+1); 
Plot(V,"",2,styleHistogram); 
Plot(IIf(Vspike ,V,Null),"",1,styleArea); 
FirstVisibleBar = Status( "FirstVisibleBar" ); 
Lastvisiblebar = Status("LastVisibleBar"); 
TN=DateTime(); 
S=Second(); 
for( b = Firstvisiblebar; b <= Lastvisiblebar AND b < BarCount; b++) 
{ 
if(VSpike[b]) PlotText( 
"\n"+NumToStr(V[b]/100*Interval(),1.0,False)+ 
"\n"+NumToStr(BS[b],1.0,False)+ 
"\n"+NumToStr(S[b],1.0,False),b,V[b],2); 
} 
Title = "\nInteractive Brokers "+Filename + " - Display Raw data in 5-Second time frame\n"+ 
"Histogram labeling:\n"+ 
" Volume/100\n Barssince last Volume update\n Second Timestamp";

This code produced the next two charts below. A simple spike filter (see the VSpike definition in the code) is used to identify Volume spikes and make them stand out with a Black background. Since these volume spikes do not appear in backfilled data, we can assume that they do not reflect true market activity. The three numbers at the top of the histogram bars, from the top down, show the Volume/100, number of bars since the last volume spike, and the Second count derived from the data time stamp.

clip_image006[16]

Figure 3 – Real-time collected volume data

Applying the code on backfilled data produces the chart below. Note that many of the low volume periods between the spikes have been filled in (it appears that the volume spikes have been retroactively distributed) and that there is no longer any visible volume periodicity.

clip_image008[16]

Figure 4 - Backfilled volume data

Comparing Data from different Databases

You can compare data from different databases in a single chart. Overlaying two data arrays will immediately reveal differences and will also suggest more sophisticated analysis to be performed. The code below can be executed by itself, or it can be appended to any other program. In this case it is coded for Volume comparison. However, you can easily modify it to compare price, indicators, or any other array. The SetBarsRequired() statement is necessary for data alignment. You must use the same timeframe for both RT and BF charts and for composite creation. All tests in this post were performed in the 5 second timeframe.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function StaticVarArraySet( Varname, array ) 
{ 
AddToComposite( array, "~SA_"+VarName, "C", atcFlagDefaults | atcFlagEnableInBacktest | atcFlagEnableInExplore | atcFlagEnableInIndicator | atcFlagEnableInPortfolio ); 
} 
function StaticVarArrayGet( VarName ) 
{ 
return Foreign("~SA_"+VarName,"C"); 
} 
SetBarsRequired(1000000,1000); 
GraphZOrder = 1; 
StaticArrayName = ParamList("Static Array Name","RawDataSample|BackfillDataSample",0); 
if(ParamTrigger("Create Volume Composite","CREATE") ) 
{ 
StaticVarArraySet( StaticArrayName, V); 
} 
if( ParamToggle("Overlay Composite","NO|YES",0) ) 
{ 
Plot(StaticVarArrayGet( StaticArrayName),"",colorYellow,styleStaircase); 
}

To compare BF with RT volume arrays, you first create the composite for the BF volume and copy this to your RT database for comparison. The procedure is as follows:

  1. Load up the database containing your BF data sample.
  2. Display the data and open the Param window:

clip_image010[16]

  1. Select BackFillDataSample for static variable name.
  2. Click CREATE.
  3. In the Amibroker menu bar, click View -> Refresh All.
  4. In the Indicator window, set Overlay Composite to YES. The composite data should display as a Yellow staircase superimposed on your volume chart.
  5. Close AmiBroker.
  6. Use Windows Explore to find your BF database and copy the composite for BF volume from the “_” folder and paste it into the “_” folder of the RT database.
  7. Delete the Broker.Master file from the RT database. This file will be recreated at next startup. This step is needed to include the new composite file in the database index.
  8. Start up AmiBroker and load up the RT database.
  9. Display the RT volume chart you were working with. If the Parameters are set as shown in the capture above you should now see the Yellow staircase for BF Volumes superimposed on the RT volume histogram.

At this point you can scroll back and forth in time to see how BF volume differs from RT collected volume. Do not click CREATE, or you will overwrite the BF composite. The charts below show what your charts should look like.

clip_image012[16]

Figure 5 - BF composite (Yellow) on BF Volume Histogram

Figure 5 above shows a period where the composite covered backfilled volume (for example the backfill period before RT collection). Because the composite copied this BF data, they match perfectly.

clip_image014[16]

Figure 6 - BF Composite (Yellow) on RT collected Volume Histogram

Figure 6 above is for a period where the composite (backfilled volume) is superimposed on the real-time collected volume (histogram). Note the difference between the two types of data.

Developing a trading system should start with learning about the basics; delays and bad data quality can kill any HFAT trading system no matter how much time you spent developing it. The best way to understand and know what you are working with is to write a few small programs, like those that were included in this series.

Conclusion

In the previous discussions, it became clear that developing an HFAT trading system might not be as easy as you think. Googling for information will reveal very few links to practical information; you’ll be mostly on your own to discover the pitfalls. Developing with live data from your paper-trading account may be better than using backfilled data. However, since it is highly likely that IB executes paper trades subject to the reported price and volume you see, paper-trading results may not match actual trading results. Unless you are acutely aware of the various problems and can develop your system to work around them, it would appear futile to try and develop an HFAT trading system with 5-second IB data. The unique real-time volume patterns also occurred in data collected from the real-trading account.

Data from all sources will have their own unique problems, and it is prudent to perform some basic testing to get to know your RT data before spending considerable time on development.

Note

IB Snapshots and data compression methods are relevant to the above discussion; even though there isn’t much detail available, you may want to read the following threads to learn more about these topics.

AmiBroker user group: Interactive Brokers Plug-in dropping volume data
IB’s Discussion Board: Globex Ticks snapshot or reality?
AmiBroker User Group: AB Tick Bar Analysis

Edited by Al Venosa.

1 Star2 Stars3 Stars4 Stars5 Stars (4 votes, average: 4.75 out of 5)
Loading ... Loading ...

Buy And Hold

FIRST DRAFT - SUBJECT TO CHANGE

OBJECTIVE

 

INTRO

Uses and limitations of B&H - benchmarking - (to be added?????????????????????)

Stepping through the process of emulating a Buy&Hold (B&H) on a single stock - initially excluding the back-tester (BT)  (working only with indicators) and then comparing the ‘manual’ B&H to the methods/results obtained in the BT- using indicator plots for visual confirmation and proving the formulas.

The manual emulation versus back-test comparison is then repeated for a simple ‘trading system’  - a set of buy (entry) and sell (exit) rules.

basic conditions (for system) - to simplify the explanation for newer users == 100% of equity per trade and enter one trade at a time (additional signals will be ignored) - reverse of the entry signal is the exit - no stops required - (BT is capable of advanced analysis - not considered in this example).

Using ABK, which was chosen at random from a US-Stocks database with 10 years of history (the formulas can be used on any stock with any number of bars).

MANUALLY CALCULATING THE B&H OUTCOME FOR A SINGLE STOCK

The B&H strategy, in it’s simplest form, buys a fixed $ value of shares and calculates their relative value at a later time (any time) - time frame can be anything - daily, weekly, monthly. This example uses EOD daily bars. In order to compare the results across periods the entry and exit are nominally made at the same point for each bar (for this example the close is used - since the close of the last bar, for the period under consideration, is the most convenient time to exit the strategy, the close on the first bar is the corresponding entry point (technically introduces a small possible error when using B&H as a benchmark for system evaluation). 

Based on that it is necessary to know the close on the first bar. The following code shows how that can be done in AFL. 

Note: Whether or not the shares ‘purchased’ are actually ’sold’ on the close of the final bar is irrelevant, since the purchase and sale of the shares is only a nominal exercise (the outcomes may vary slightly from AB mode to mode, depending on whether the ’sale’ is encoded in AFL, or not, and so this drives the need to ’sell’ or ‘not to sell’ e.g. if the user wants to plot a buy and sell arrow on the chart and the code does that accurately, for the current mode, then that is ‘good’ code for that mode).

1
2
3
4
5
6
7
//P_BuyAndHold v4 
//STEP 1A 
//right click on the formula and select Edit to open it in the Formula Editor (FE) 
//insert it as an indicator in its own pane (from the FE toolbar) 
//do not close the FE - keep it open and edit the code to step through the examples (the plot in the bottom pane will change after every FE save). 
B = BeginValue(Close);  
Plot(B,_DEFAULT_NAME(),colorBlack,1)

The close on the first bar is 19.78 - BeginValue plots this as a constant for the entire 10 year period.

Figure 1

BAndH001

BeginValue can be used to manually calculate the equity curve for a Buy&Hold strategy.

1
2
3
4
5
6
7
//P_BuyAndHold  
//STEP 1B 
//manually enter the initial equity - in this case 100,000 
B = BeginValue(Close);
InitialEquity = 100000;  
BH = InitialEquity/B * Close;  
Plot(BH,_DEFAULT_NAME(),colorBlack,1);

 Figure 2

BAndH002

The number of shares purchased at the close on the first bar can be calculated.

Note: Decimal places displayed can be changed in Tools >> Preferences (normally two deci places is all the that is required but can be increased for this demonstration). 

Shares purchased == Initial Equity/Initial Price

                             == 100000/19.78

                             == 5055.611729

Fractional shares can’t be purchased in ‘real life’ but, to allow for accurate mathematical computation, it is nominally permitted in AB (Automatic Analysis).

The value of 5055.xx shares can be calculated at any time by multiplying by the current close price e.g. referring to the bar marked on the chart by the selector line.

BuyAndHold Equity == shares held (purchased) * current price

                               == (100000/19.78) * 22.41 

                               == 113296.25 (which is consistent with the method used in Step 1A and plotted in Figure 2).

 

The expression BuyAndHold Equity ==  (Initial Equity/Initial Price) * current price can be also restated:

BuyAndHold Equity == Initial Equity * (Current Price/Initial Price)

So the Buy& Hold Equity, is always proportional to the ratio of the Current Price to the Initial Price, which is a relative measure.

1
2
3
4
5
6
7
8
//P_BuyAndHold v4  
//STEP 2A 
//B = BeginValue(Close);
InitialEquity = 100000;  
//BH = (InitialEquity/B) * Close;  
//Plot(BH,_DEFAULT_NAME(),colorBlack,1);  
eRatio = Close/BeginValue(Close);
Plot(eRatio,"PseudoEquity",colorRed,1);

Figure 3

BAndH006

At the bar marked on the chart, by the selector line, there is a 13.29% gain in the Buy&Hold equity ratio (if the Initial Equity was 100,000 the B&H equity curve would be 113,296.3 - the same as that calculated value in Figure 2, at the same point in time).

1
2
3
4
5
6
7
8
//STEP 2B
//manually enter the initial equity - in this case 100,000  
//B = BeginValue(Close);
InitialEquity = 100000;
//BH = (InitialEquity/B) * Close;
//Plot(BH,_DEFAULT_NAME(),colorBlack,1);  
eRatio = Close/BeginValue(Close);
Plot(eRatio * InitialEquity,"PseudoEquity",colorRed,1);

Figure 4

BAndH023

AmiBroker has a built-in function that automatically calculates equity.

Note: The Equity() function is a special function that is part of the Automatic Analyzer suite and it references various AA settings (normally it will automatically use the default AA settings but for this example they will be ’manually’ overwritten within the code).

1
2
3
4
5
6
7
8
9
10
11
//STEP 3  
//adapted from code by Tomasz Janeczko
//manually enter the Initial Equity value as the second SetOption parameter  
Buy =  1;
SetOption("InitialEquity", 100000 );
PositionSize = -100;
SetTradeDelays( 0,0,0,0);
ApplyStop(0, 0,0,0);
ApplyStop(1, 0,0,0);
ApplyStop(2, 0,0,0);
Plot( Equity(0,0), "Equity",colorGreen,1 );

Note: In this mode (Indicator plots)  it is irrelevant whether the Sell is included in the code or not.

The outcome is exactly the same as in the previous ‘manual’ equity calculations.

BAndH024

In the default BT mode, redundant symbols are ignored when calculating Equity().

To demonstrate this:

If Buy =1 is plotted, with shapes, it produces a buy arrow on every bar (the Buy expression is always true).

1
2
3
4
5
//P_BuyAndHoldOverlay  
//save as a separate formula and overlay on a price chart  
Buy =  1;  
shape = Buy * shapeUpArrow;
PlotShapes( shape,colorGreen,Low );

BAndH009

Adding the ExRem function allows users to see the Buy signals as they are processed by the Automatic Analyzer, in tandem with the equity() function.

1
2
3
4
5
6
7
8
//P_BuyAndHoldOverlay  
//save as a separate formula and overlay on a price chart  
Buy =  1;
Sell = 0;  
Buy = ExRem(Buy,Sell);
Sell = ExRem(Sell,Buy);  
shape = Buy * shapeUpArrow + Sell * shapeDownArrow;
PlotShapes( shape, IIf( Buy, colorGreen, colorRed ), 0, IIf( Buy, Low, High ) );

Note: In indicator mode the Sell = 0; expression is redundant when calculating Equity(). It has been included in this example as it is conditional when using the ExRem() function.

BAndH010

BUY AND HOLD ON A SINGLE STOCK USING THE AUTOMATIC ANALYZER

Zooming to full range on the ABK chart and placing the selector line on the last bar brings up the final value of the Buy&Hold equity ‘curve’ for full number of bars i.e. 434,175.91

Note: Any of the ‘manual’ methods demonstrated will produce the same ‘final’ result.

Figure ????

BAndH025

The back-tester can also be used to calculate the B&H outcome.

1) First setup the chart to display trading arrows after the back-test.

 Right click inside the current price chart and pick Show trading arrows

BAndH014

2) Enter AA Settings

100000 Initial Equity, Long, Daily, Round lot size = 0 and no commission (0)

Min shares set to additional decimal points allows for more accurate calculations (depending on the situation??? not required for this example now?????).

BAndH015

plus defaults - should be nothing to change - - the other settings are more or less arbitrary for this example.

BAndH016

BAndH017

1
2
3
4
5
//B_BuyAndHold  
//run as an Individual Backtest on current stock, all quotes  
Buy = 1;
Sell = 0;  
PositionSize = -100;

Back-test by clicking on the Back Test button and selecting Individual Backtest.

BAndH018

To plot the trade arrows from the back-test.

Right click inside Results pane and select Show arrows for actual trades

BAndH019

To plot the equity curve for a single stock back-test.

Drop-down Equity button and select Individual Equity.

BAndH020

The resultant chart shows that the trade was entered on the first bar and sold on the last and that the Buy&Hold equity matches the (yellow) Equity() curve exactly (positioning the selector bar on the last line also shows that it tallies with the B&H equity values calculated earlier in the post).

BAndH013

Right clicking inside the pane, that contains the Equity() plot, and selecting Edit Formula reveals the AmiBroker code that produced the graph.

BAndH022

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
_SECTION_BEGIN("Individual");
#include @LastBacktestFormula
MaxGraph=0;GraphXSpace=5;
GraphZOrder=1;
Plot( Equity( 0, -2 ), "Equity", -8, styleArea );  
if( ParamToggle("Show Buy-and-Hold?", "No|Yes", 1 ) )
{
 /* now buy and hold simulation */
 Short=Cover=0;
 Buy=Status("firstbarintest");
 Sell=Status("lastbarintest");
 SetTradeDelays(0,0,0,0); PositionSize = -100;
 ApplyStop(0,0,0,0);
 ApplyStop(1,0,0,0);
 ApplyStop(2,0,0,0);
 Plot( Equity( 0, -2 ), "Buy&amp;Hold", -9 );
}
_SECTION_END();

 

 

COMPARING BUY AND HOLD TO A TRADING SYSTEM

ADD A 3MA EQUITY CURVE

The following code meets the buy conditions as described by Walter (they can be changed to anything).

1
2
3
4
5
6
7
8
//ADD EQUITY CURVE FOR 3MA TRADING SYSTEM  
Condition1 = Ref(Close,-1) > Ref(MA(C,40),-1);
Condition2 = Ref(Close,-1) > Ref(MA(C,80),-1);
Condition3 = Ref(Close,-1) > Ref(MA(C,200),-1);  
Buy =  Condition1 == 1 AND Condition2 == 1 AND  Condition3 == 1;
Sell = Condition1 == 0 AND Condition2 == 0 AND  Condition3 == 0;  
shape = Buy * shapeUpArrow + Sell * shapeDownArrow;
PlotShapes( shape, IIf( Buy, colorGreen, colorRed ), 0, IIf( Buy, Low, High ) );

When plotted as an overlay all signals are shown - the arrows mark when the close is above, or below, all three MA’s as requested.

Note: it is looking back to the previous bar since the entry will be on to-days open if yesterday reports a buy signal.

BAndH004

The code is modified to remove extra signals since in ‘real life’ we can only be in one trade at a time.

1
2
3
4
5
6
7
8
//ADD EQUITY CURVE FOR 3MA TRADING SYSTEM  
Condition1 = Ref(Close,-1) > Ref(MA(C,40),-1);
Condition2 = Ref(Close,-1) > Ref(MA(C,80),-1);
Condition3 = Ref(Close,-1) > Ref(MA(C,200),-1);  
Buy =  Condition1 == 1 AND Condition2 == 1 AND  Condition3 == 1;
Sell = Condition1 == 0 AND Condition2 == 0 AND  Condition3 == 0;  
shape = ExRem(Buy,Sell) * shapeUpArrow + ExRem(Sell,Buy) * shapeDownArrow;
PlotShapes( shape, IIf( Buy, colorGreen, colorRed ), 0, IIf( Buy, Low, High ) );

Removes extra signals marked by the arrows -

Note: that there is a penalty period for the first 200 bars while we wait for the 200 MA to start.

Also code is an approximation as it says above and it does not say cross (this would need more advanced code but I will keep it simple to start).

BAndH005

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...