Backtesting a Moving Average Crossover i Python med pandas I den tidigare artikeln om Research Backtesting Environments I Python With Pandas skapade vi en objektorienterad forskningsbaserad backtestingmiljö och testade den på en slumpmässig prognosstrategi. I den här artikeln kommer vi att använda sig av den maskin som vi introducerade för att utföra forskning om en faktisk strategi, nämligen Moving Average Crossover på AAPL. Flyttande genomsnittlig Crossover-strategi Den Moving Average Crossover-tekniken är en extremt välkänd förenklad momentumstrategi. Det anses ofta Hello World-exemplet för kvantitativ handel. Strategin som beskrivs här är långsiktig. Två separata enkla glidande medelfilter skapas, med varierande återkänningsperioder, för en viss tidsserie. Signaler för att köpa tillgången inträffar när det kortare återgåande glidande medlet överstiger det längre återkommande glidmedlet. Om det längre genomsnittet därefter överstiger det kortare genomsnittet säljs tillgången tillbaka. Strategin fungerar bra när en tidsserie går in i en period med stark trend och sedan sakta tillbaka trenden. I det här exemplet har jag valt Apple, Inc. (AAPL) som tidsserier, med en kort lookback på 100 dagar och en lång återgång på 400 dagar. Detta är exemplet från zipline algoritmiska handelsbiblioteket. Således, om vi vill implementera vår egen backtester måste vi se till att den matchar resultaten i zipline, som ett grundläggande sätt att validera. Genomförande Se till att du följer den tidigare handledningen här. som beskriver hur den ursprungliga objekthierarkin för backtesteren är konstruerad, annars fungerar inte koden nedan. För detta specifika genomförande har jag använt följande bibliotek: Implementeringen av macross. py kräver backtest. py från föregående handledning. Det första steget är att importera de nödvändiga modulerna och objekten: Som i föregående handledning kommer vi att subklassera abstraktionsklassen Strategi för att producera MovingAverageCrossStrategy. som innehåller alla detaljer om hur man genererar signalerna när de rörliga medelvärdena av AAPL passerar över varandra. Objektet kräver en shortwindow och en longwindow att fungera för. Värdena har ställts till standardvärden på 100 dagar respektive 400 dagar, vilka är samma parametrar som används i huvudexemplet för zipline. De glidande medelvärdena skapas genom att använda pandas rollingmean-funktionen på staplarna. Slutkursen för AAPL-aktien. När de individuella rörliga medeltalen har konstruerats genereras signalen Serie genom att kolumnen är lika med 1,0 när det korta glidande medlet är större än det långa glidande medlet eller 0,0 annars. Från detta kan positioneringsorder genereras för att representera handelssignaler. MarketOnClosePortfolio är subclassed från Portfolio. som finns i backtest. py. Det är nästan identiskt med det genomförande som beskrivs i den föregående handledningen, med det undantaget att handeln nu utförs på nära håll, snarare än en öppen till öppen basis. Mer information om hur Portfolio-objektet definieras finns i föregående handledning. Ive lämnade koden för fullständighet och för att hålla denna handledning fristående: Nu när klasserna MovingAverageCrossStrategy och MarketOnClosePortfolio har definierats kommer en huvudfunktion att kallas för att binda alla funktioner tillsammans. Dessutom kommer strategins resultat att undersökas via en kurva av aktiekurvan. Pandas DataReader-objekt laddar ner OHLCV-priser på AAPL-lager för perioden 1 januari 1990 till 1 januari 2002, vid vilken tidpunkt signalerna DataFrame skapas för att generera de långsiktiga signalerna. Därefter genereras portföljen med en initialkapital på 100 000 USD och avkastningen beräknas på egenkapitalkurvan. Det slutgiltiga steget är att använda matplotlib för att rita en tvåsiffrigt plot av båda AAPL-priserna, överlagrade med de glidande medelvärdena och buysell-signalerna, såväl som kapitalkurvan med samma buysell-signaler. Plottingskoden tas (och modifieras) från exempelsegmentet för zipline. Kodens grafiska utgång är enligt följande. Jag använde kommandot IPython-klistra för att placera detta direkt i IPython-konsolen i Ubuntu, så att den grafiska produktionen förblev i sikte. De pink upticksna representerar att köpa beståndet, medan de svarta downtickarna representerar att sälja tillbaka: Som det kan ses, förlorar strategin pengar under perioden, med fem rundturer. Detta är inte förvånande med tanke på AAPL: s beteende under perioden, vilket var en liten nedåtgående trend följt av en signifikant uppgång som började 1998. Utsläppsperioden för de rörliga genomsnittssignalerna är ganska stor och detta påverkade slutproduktens vinst , vilket annars kan ha gjort strategin lönsam. I efterföljande artiklar kommer vi att skapa ett mer sofistikerat sätt att analysera prestanda, samt beskriva hur man optimerar utkikningsperioderna för de individuella rörliga genomsnittssignalerna. Just Komma igång med Kvantitativ TradingMoving Genomsnittlig Crossover Trading Strategy Backtest i Python 8211 V 2.0 Detta är en testannonstext Välkommen back8230This inlägget kommer att hantera ett par frågor som jag fick i kommentarfältet i ett tidigare inlägg, en som rör rörelse genomsnittlig crossover trading strategi 8211 artikeln kan hittas här. Frågorna jag fick först frågade om koden för backtest kunde anpassas för att följa följande regler: (1) Om det korta glidande medelvärdet korsar över det långa glidande medeltalet går längtan efter x dagar. (2) om det korta glidande medelvärdet korsar det långa glidande medeltalet kort för x dagar. (3a) Om det finns en extra korsning under innehavsperioden ignorera det (3b) Om det inte finns några korsningar, håll kontant. Här är originalkoden: importera relevanta moduler importera pandor som pd import numpy som np från pandasdatareader importdata från matteimport matplotlib. pyplot som plt ladda ner data till DataFrame och skapa glidande medelvärden kolumner sp500 data. DataReader (GSPC, yahoo, start112000) sp50042d np. round (sp500Close. rolling (window42).mean (), 2) sp500252d np. round (sp500Close. rulla (window252).mean (), 2) skapa kolumn med rörlig genomsnitts spridningsskillnad sp50042-252 sp50042d - sp500252d ställa in önskat antal punkter som tröskel för spridningsskillnad och skapa kolumn innehållande strategi Stance X 50 sp500Stance np. where (sp50042-252 gt x, 1, 0) sp500Stance np. where (sp50042-252 lt X, -1, sp500Stance) sp500Stance. valuecounts () skapa kolumner som innehåller dagliga marknadsloggaregistreringar och strategi dagliga loggar returnerar sp500Market Retur np. log (sp500Close sp500Close. shift (1)) s p500Strategy sp500Market Returnerar sp500Stance. shift (1) inställt strategiskt startkapital till 1 (d. v.s. 100) och generera kapitalkurva sp500Strategy Equity sp500Strategy. cumsum () 1 visa diagram över kapitalkurvan sp500Strategy Equity. plot () Och här är koden som innehåller ovanstående regler: importera relevanta moduler importera pandor som pd import numpy som np från pandasdatareader import data från matematik import sqrt import matplotlib. pyplot som plt matplotlib inline ladda ner data till DataFrame och skapa glidande medelvärden kolumner sp500 data. DataReader (GSPC, yahoo, start112014) sp50042d np. round (sp500Close. rolling (window42).mean (), 2 ) sp500252d np. round (sp500Close. rolling (window252).mean (), 2) skapa kolumn med rörlig genomsnittsspridningsskillnad sp50042-252 sp50042d - sp500252d ställa in önskat antal poäng som tröskel för spridningsskillnad och skapa kolumn innehållande strategi Stance X 50 sp500Stance np. where (sp50042-252 gt x, 1, 0) sp500Stance np. where (sp50042-252 lt - X, -1, sp500Stance) sp500Stance. valuecounts () Skapa kolumner som innehåller dagliga marknadsloggavkastningar och strategisk daglig loggrätt urns sp500Market Returnerar np. log (sp500Close sp500Close. shift (1)) sp500Strategy sp500Market Returnerar sp500Stance. shift (1) skapa en ny kolumn för att hålla vår ståndpunkt relevant för den förutbestämda hållperioden sp500Stance2 0 angiven förutbestämd hålltid, varefter tid vi kommer att gå tillbaka till att hålla kontanter och vänta på nästa glidande medelvärde korsa - vi kommer också att ignorera eventuella extraövergångar under den här hållningsperioden dagarna 50 iterera genom DataFrame och uppdatera kvoten2quot-kolumnen för att hålla den uppenbara ståndpunkten för jag i intervallet (X , len (sp500)): logiskt test för att kontrollera 1) ett kors över kort över MA 2) Att vi för närvarande är i kontanter om (sp500Stance. iloci gt sp500Stance. iloci-1) och (sp500Stance. iloci-1 0) och (sp500Stance2.iloci-1 0): fyll i DataFrame framåt i tid för antalet dagar i vår innehavstid för k inom intervallet (dagar): försök: sp500Stance2.ilocik 1 sp500Stance2.ilocik1 0 utom: passera logiskt test för att kontrollera för 1) ett kors över kort under lång MA 2) T hatt vi är för närvarande i pengar om (sp500Stance. iloci lt sp500Stance. iloci-1) och (sp500Stance. iloci-1 0) och (sp500Stance2.iloci-1 0): fyll i DataFrame framåt i tid för hur mycket dagar i vår innehavsperiod för k i intervallet (dagar): försök: sp500Stance2.ilocik -1 sp500Stance2.ilocik1 0 utom: passera Beräkna daglig avkastning på marknaden och strategi dagliga avkastning sp500Market Retur np. log (sp500Close sp500Close. shift (1)) sp500Strategy sp500Market Returnerar sp500Stance2.shift (1) diagrammet returnerar mot marknadsavkastning sp500Market Returns, Strategy. cumsum (). plot (gridTrue, figsize (8,5)) plt. show () fastställt strategi startande kapital till 1 (dvs. 100) och generera egenkapitalkurva sp500Strategy Equity sp500Strategy. cumsum () 1 visa diagram över kapitalkurvan sp500Strategy Equity. plot (gridTrue, figsize (8,5)) plt. show () Efter att ha skickat denna kod fick jag följande uppföljningsfrågor : 8220Tack för svaret. Jag har problem med att förstå den här koden. Koden fungerar men jag skulle vilja förstå det bättre. Jag är främst förvirrad med iloc och k och I. Jag förstår verkligen inte vad de är eller var de drar information från. någon tydlighet skulle uppskattas mycket8221 So8230. Här är mitt försök att försöka klargöra mitt tänkande medan du skriver koden och försöka förklara exakt vad som händer på rad. Let8217s börjar med 8220iloc8221 här är informationen från den officiella Pandas dokumentationen:.iloc är i första hand heltalspositionsbaserad (från 0 till längd 1 av axeln), men kan också användas med en booleskt array. iloc kommer att höja IndexError om en efterfrågad indexer är out-of-bounds, förutom segmentindexers som tillåter indexering utan gränser. (detta överensstämmer med pythonnumpy-segmentets semantik). Tillåtna ingångar är: Ett heltal, t. ex. 5 En lista eller array av heltal 4, 3, 0 Ett skivobjekt med ints 1: 7 En boolean array En kallbar funktion med ett argument (den anropande serien, DataFrame eller Panel) och som returnerar giltig utgång för indexering (en av ovanstående ) Såsom nämnts ovan är 8220iloc8221 bara ett enkelt sätt att referera till en radposition i din DataFrame 8211 enkelt som det. Om vi skapar följande DataFrame och visar det, får vi: iterera genom DataFrame och uppdatera kolumnen quotStance2quot för att hålla den uppenbara ståndpunkten för jag i intervallet (X, len (sp500)): Används 8220range8221-funktionen för att skapa en lista över värden som börjar vid 8220X8221 och går upp till (men inte inklusive) värdet av hur länge sp500 DataFrame är, med funktionen 8220len8221. Så om X exempelvis är 50 och längden på Sp500 DataFrame är 500, kommer listan över värden som vi kommer att iterera genom att springa från 50 till 499 8211, dvs från den 50: e dagen i vår DataFrame till den sista. När vi kombinerar 8220.iloc8221 och 8220 för jag i range8221 och faktiskt förmedlar värdet 8220i8221 till 8220iloc8221 som 8220.iloci8221 8211 kan vi använda iteratorn 8220i8221, vilket ökar med en varje gång loopbandet körs, för åtkomst till varje DataFrame-radindex, en efter en. Höger, så vi borde nu vara tydliga om de viktigaste 8220 för jag i intervallet (X, len (sp500)): 8221 loop 8211 vi bokstavligen bara repeterar men varje rad i DataFrame en efter en, börjar vid rad X och slutar vid slutet av DataFrame (faktiskt tekniskt slutar vi på den andra sista dagen, eftersom intervallfunktionen skapar en lista som går upp till, men doesn8217t inkluderar det andra värdet som passerade 8211 men för vårt ändamål slutar det på den andra sista dagen ingen effekt på output) Så nu let8217s försöker ta itu med alla dessa 8220if8221 och 8220for k8221 loopar och test inom den huvudsakliga yttre 8220for8221-slingan som förklaras ovan. 8220if8221-testerna kör bara ett par logiska tester på varje rad i DataFrame som vi repeterar genom 8211 de första logiska testerna behandlar överkorsningar när det korta glidande medelvärdet överstiger det långvariga genomsnittet och använder värdena i kolumnen 8220Stance8221 för det första för att kontrollera om värdena flyttas från 0 till 1 från dag 8220T-18221 till dag 8220T8221 OCH värdet i 8220Stance28221-kolumnen vid dag 8220T-18221 är också 0 8211 Om så betyder det att det korta glidande medlet har passerat över den långa rörelsen genomsnittet och vi borde gå in i en position. Om det här är isn8217t klargör varför 8211 ta lite tid att tänka igenom logiken. Vi behöver kolumnen 8220Stance8221 för att flytta från 0 till 1 för att indikera en kort över lång MA-crossover, men vi behöver också 8220Stance28221 kolumnen på dag 8220T-18221 vara 0, som om det inte finns någon position för närvarande och man bör därför ingå i. Om 8220Stance28221 vid dag 8220T-18221 varn8217t noll, då är de redan i position och inget bör göras, trots att det första logiska testet visar ett glidande medelvärde. Jag uppskattar att det kan ta lite tid att arbeta igenom och ta sig runt, men allt jag kan säga är att de logiska testen som uppställs i koden verkligen följer logiken som presenteras i de inledande frågorna. Den andra uppsättningen 8220if8221-tester är bara spegelbilden av den första 8211 och hanterar övergångar när det korta glidande medelvärdet passerar under det långa glidande medeltalet. Slutligen är det förhoppningsvis ovanstående allt klart (som lera) och allt vi har kvar för att hantera är användningen av 8220k8221 i 8220for k intervallet (dagar) 8221 loop. För det första är 8220days8221 bara en variabel som innehåller antalet dagar vi vill behålla våra positioner för innan du automatiskt lämnar dessa positioner. 8220days8221 sattes till 50 i koden, men kunde ställas in på vilket värde man önskar för sin specifika strategi. Så med det i åtanke går 8220for k-serien (dagar) 8221-slingan om och endast om de tidigare logiska testerna är nöjda, och vår strategi säger att vi ska öppna en position (vare sig lång eller kort). Om en ingångssignal presenteras springer vi sedan framåt i tiden med 8220k8221 dagar (från och med dag 8220i8221 8211 så vi använder 8220ik8221 som startdag) och ställer in vår 8220Stance28221 till antingen 1 eller -1 (beroende på om signalen är lång eller kort positionssignal). Detta representerar den tid vi vill behålla vår position innan du automatiskt lämnar. Så om en ingångssignal produceras på dag 100 (vid vilken tidpunkt jag kommer att vara lika med 100), ställs ståndpunkten vid dag 8220i k8221 till antingen -1 eller 1 k börjar vid 0 och går till 49, stegvis med en med varje springa igenom 8220for k in8221 loop. Så ståndpunkten vid dag 8220i k8221 kommer att sätta dag 100 0 till 1 eller -1, sedan dag 100 1 sedan 100 2 etc, tills positionen är avslutad vid dag 100 49 och ståndpunkten på dag 100 50 är inställd på noll till visa en platt position. Ett par möjligen förvirrande tillägg till logiken ovan och i följande rader: för k i intervallet (dagar): försök: sp500Stance2.ilocik 1 sp500Stance2.ilocik1 0 förutom: pass är användningen av den andra raden 8220sp5008216Stance28217.ilocik1 08221 8211 I har inkluderat detta för att se till att kolumnen 8220Stance28221 återställs till 0 när vi har lämnat vår position och är nu platta igen 8211 är denna platta inställning inställd dagen efter att vi lämnar vår position 8211 i effektdag (ik1). Det andra möjligen förvirrande tillägget är användningen av 8220try och except8221. Vi använder detta så att om vi når slutet av DataFrame och ändå springer framåt i tiden med X dagar istället för att kasta upp ett 8220index out of bounds8221-fel och avslutar koden, passerar koden bara och slutar utan fel. Det här kan hända till exempel om vi säger att vårt fönster 8220days8221 är inställt på 50 och med endast 30 dagar kvar av data, skapas en ingångssignal 8211 om vi försöker iterera framåt 50 dagar i tid då endast 30 dagar data finns 8211 utan tryexcept vår kod kommer att speka ett fel och avsluta. Förhoppningsvis förklarar och förklarar det här inlägget den överväldigande majoriteten av den utvidgade koden 8211, om inte, vänligen lämna dina frågor och kommentarer nedan och I8217ll försöker ta itu med dem all8230 Till nästa gång Bra info. Men jag måste notera att tidigare webbdesign var mycket mycket bättre än den här. Den grå texten är svår att läsa, zoom inout-funktioner fungerar inte på det här temat. Hela webbplatsen fungerar långsammare än med föregående tema. Kanske eftersom flera plugins installerats som Squirrly SEO, W3 Total Cache och Jetpack. Beleiva mig, med sådant informativt innehåll, behöver du inte alls det som soptipp. Hej där8230 Jag är helt överens, I8217ve gav upp med föregående tema8230it var alltför komplicerat bakom kulisserna och jag kunde aldrig få någonting att se hur jag ville ha det. Så I8217ve bestämde mig för att använda det här nya temat nu skulle jag vara intresserad av att höra vad du tycker om det Tack för feedbacken 8211 it8217s uppskattas alltid och definitivt får8217s lyssnat påMoving Average Crossover Trading Strategi Backtest i Python Detta är en testannons text Hej alla, för det här inlägget kommer jag att bygga en enkel glidande genomsnittlig crossover tradingstrategi backtest i Python. använder SampP500 som marknaden för att testa på. Ett enkelt glidande medelvärde över strategin är möjligen ett av, om inte det enklaste exemplet på en reglerbaserad handelsstrategi med hjälp av tekniska indikatorer, så jag trodde att detta skulle vara ett bra exempel för dem som lär sig. Python försöker hålla det så enkelt som möjligt och bygga upp därifrån. Så som alltid när du använder Python för finansiella data relaterade shenanigans, är it8217s tid att importera våra nödvändiga moduler: Importera pandas som pd import numpy som np från pandasdatareader importdata Vi använder först pandas-datareaderfunktionaliteten för att ladda ner prisdata från den första handeln dag 2000, till idag, för SampP500 från Yahoo Finance enligt följande: sp500 data. DataReader (GSPC, yahoo, start112000) Detta är ett test av en annan VIX handelsstrategi från den utmärkta Logical-Invest (se vårt tidigare test av LIs Bollinger band). Den här använder 515-dagars glidande medelvärdesövergångar för att handla VIX ETPs som XIV (eller kort VXX). Diagrammet nedan visar resultat av strategin handel XIV (blå), jämfört med att köpa och hålla XIV (grå), från mitten av 2004. Läs om testantaganden. eller få hjälp efter denna strategi. Strategiregler: Gå lång XIV vid dagens stängning om dess fem dagars enkla glidande medelvärde (SMA) kommer att stängas över dess 15-dagars SMA. Håll kvar tills dess 5-dagars SMA stänger under dess 15-dagars SMA, och flytt sedan till kontanter. Strategin (som Bollinger-bandvariationen) är i en rotationsväg en momentumstrategi. Strategin köper XIV när den visar ny styrka och håller tills XIV går tillbaka under sitt medelfristiga medelvärde. Observera att vår backtest skiljer sig från det ursprungliga testet Logical-Invests på tre sätt: LIs-testet började i början av 2009. Weve lade till nästan 5 års ytterligare simulerade data (1). LIs-testet antog vi kortade VXX. medan Ive visade resultathandel långa XIV för att möjliggöra en jämförelse mellan äpplen och äpplen med andra backtests här på Volatility Made Simple. LIs-testet antog att vi genomförde affärer vid de närmaste dagarna öppna, snarare än vid dagens stängning. Jag testar på slutet eftersom det inte finns någon tillförlitlig metod för att simulera data före 2009 för det öppna. Som du förväntar dig intuitivt baserat på strategys regler, har du gjort ett bra jobb och sidesteglar de flesta av XIVs betydande drawdowns eftersom det tvingar strategin till pengar när XIV börjar röra sig mot näringsidkaren, oavsett alla andra överväganden som tillståndet för VIX-futures menstruation, etc. Men det är ivriga att lämna positioner snabbt och lämnar också mycket vinster på bordet när XIV står i en konsekvent uptrend (det vill säga VXX har en stark nedåtgående trend), vilket har varit fallet de senaste 2 åren. Bollinger-bandstrategin som vi testat tidigare hjälpte till att svara på det genom att göra det lite svårare att avsluta positioner (se posten för detaljer). Baserat på de mycket längre tester som Ive presenterade här, av de två variationerna, föredrar jag Bollinger-bandet en. Ett stort tack till Logical-Invest för att lägga ut denna strategi. När de strategier som vi täcker på vår blogg (inklusive den här) signalerar nya affärer, inkluderar vi en varning på den dagliga rapporten som skickas till abonnenter. Detta är helt orelaterat med vår egen strategysignal, det tjänar bara till att lägga till en liten färg i den dagliga rapporten och låter abonnenter se vilka andra kvantitativa strategier som sägs om marknaden. Klicka för att se Volatilitet Made Simples egen elegant lösning till VIX ETP pussel. God handel, volatilitet gjort Simple Wonk notering: Data före lanseringen av XIV har simulerats. Kunnat göra detta exakt genom att använda en kombination av indexen och de framtidsdata som den här ETP baseras på. Läs mer om simulering av data för VIX ETPs. Postnavigering Kategorier Senaste inlägg
No comments:
Post a Comment