How to continuously export data from Amibroker

2019-04-02 18:10发布

问题:

Need to test an algotrading tool to be used for day trading. To get the intra-day live data from NSE, some vendors are selling data plugins for Amibroker. To get the data out of Amibroker I intend to use AFL ( Amibroker Formula Language ). The following code in AFL creates the .csv files for each symbol once. But to use the live data I need to append incoming data to the .csv file continuously throughout the day. How can this be done without crashing / overloading Amibroker?

// created a directory on your C drive named AmiBroker data backup
dayhours = paramtoggle("Day hours only", "No|Yes");
fmkdir("c:\\AmiBackup\\");
setbarsrequired(100000,100000);
lname = Name(); // gets the name of the symbol
// note: if you have names with invalid characters like / you must rename the
file before you try to create a name 
// add an IF line for each symbol you need to rename
if (lname == "ER2U8-GLOBEX-FUT") lname = "ER2U8";

fh = fopen( "c:\\AmiBackup\\" + lname + ".csv", "w"); 
if( fh ) 
{ 
    if(interval() == inDaily OR interval() == inMonthly OR interval() == inweekly)
    {
        fputs( "Ticker,Date,Open,High,Low,Close,Volume \n", fh ); 
        for( i = 0; i < BarCount; i++ ) 
        { 
            y = Year(); 
            m = Month(); 
            d = Day(); 
            fputs( Name() + "," , fh );
            ds = StrFormat("%02.0f-%02.0f-%02.0f,", m[ i ], d[ i ], y[ i ] ); 
            fputs( ds, fh ); 
            qs = StrFormat("%.4f,%.4f,%.4f,%.4f,%.0f\n", O[ i ],H[ i ],L[ i ],C[ i
],V[ i ] ); 
            fputs( qs, fh ); 
            if(i == 65500 or i == 130000 or i == 196500 or i == 262000)
            {
                fclose( fh ); 
                if(i == 65500  ) fh = fopen( "c:\\AmiBackup\\" + lname + " A.csv", "w"); 
                if(i == 130000 ) fh = fopen( "c:\\AmiBackup\\" + lname + " B.csv", "w"); 
                if(i == 196500 ) fh = fopen( "c:\\AmiBackup\\" + lname + " C.csv", "w"); 
                if(i == 262000 ) fh = fopen( "c:\\AmiBackup\\" + lname + " D.csv", "w"); 
            }
        }
    }
    else // intraday so add time field
    {
        fputs( "Ticker,Date,Time,Open,High,Low,Close,Volume \n", fh ); 
        y = Year(); 
        m = Month(); 
        d = Day(); 
        r = Hour();
        e = Minute();
        n = Second();

        for( i = 1; i < BarCount; i++ ) 
        { 
            if (dayhours and lastvalue(timenum()) >= 92900 and lastvalue(timenum()) <=
161500)
            { 
                fputs( Name() + "," , fh );
                ds = StrFormat("%02.0f-%02.0f-%02.0f,", m[ i ], d[ i ], y[ i ] ); 
                fputs( ds, fh ); 

                ts = StrFormat("%02.0f:%02.0f:%02.0f,", r[ i ],e[ i ],n[ i ] ); 
                fputs( ts, fh ); 

                qs = StrFormat("%.4f,%.4f,%.4f,%.4f,%.0f\n", O[ i ],H[ i ],L[ i ],C[ i
],V[ i ] ); 
                fputs( qs, fh ); 
            }
            else
            { 
                fputs( Name() + "," , fh );
                ds = StrFormat("%02.0f-%02.0f-%02.0f,", m[ i ], d[ i ], y[ i ] ); 
                fputs( ds, fh ); 

                ts = StrFormat("%02.0f:%02.0f:%02.0f,", r[ i ],e[ i ],n[ i ] ); 
                fputs( ts, fh ); 

                qs = StrFormat("%.4f,%.4f,%.4f,%.4f,%.0f\n", O[ i ],H[ i ],L[ i ],C[ i
],V[ i ] ); 
                fputs( qs, fh ); 
            }
            if(i == 65500 or i == 130000 or i == 196500 or i == 262000)
            {
                fclose( fh ); 
                if(i == 65500  ) fh = fopen( "c:\\AmiBackup\\" + lname + " A.csv", "w"); 
                if(i == 130000 ) fh = fopen( "c:\\AmiBackup\\" + lname + " B.csv", "w"); 
                if(i == 196500 ) fh = fopen( "c:\\AmiBackup\\" + lname + " C.csv", "w"); 
                if(i == 262000 ) fh = fopen( "c:\\AmiBackup\\" + lname + " D.csv", "w"); 
            }
        } 
    }
    fclose( fh ); 
} 

Buy = 1;

回答1:

It seems you're continuously asking the server for data regarding 100.000 bars, whilst it only needs the last.

I would create this overview once, set a flag in the code wether this had been done, and when done only update the data by iterating backwards until received barOpen time equals the bars already in the overview.



回答2:

Get rid of your for loops. Amibroker runs the AFL every time your chart is updated, so with those for loops, you're going through each bar that's on the database every time the chart updates (when a new quote comes in), so the more bars you have, the higher your memory usage. You can just set i = 0 to reference the current bar. I'm just going off the top of my head. If you don't come right, and don't mind writing a C# dll, let me know, I'll post the code.



回答3:

No problem.

I got the idea to do this from

https://adamprescott.net/2012/04/05/net-vb6-interop-tutorial/

What you're going to do is create a class library. Create an interface and a class that implements the interface. In your case, it would be something like this:

namespace AmiCOMLib
{
    [ComVisible(true)]
    public interface IMyInterop
    {
        [DispId(1)]
        void SendQuotes(string quoteString);
    }//end interface  

    [ComVisible(true)]
    public class AmiCOM
    {
        public void SendQuotes(string quoteString)
        { 
            //process string here and save as CSV
        }
    }
  }

You can send the quote string as a comma separated string (like I usually do for long strings) and split it in your DLL or send with many parameters.

Next you'll prepare to register your DLL. If the DLL and Amibroker are on the same machine, you can use the Build Events, otherwise, you can create a batch file. The first time you run this, run the register part first (comment out the unregister code). Every time I recompile my DLL, I run the unregister then register code. I'm not sure if it's necessary, but I do it anyway. The targetpath can be any folder you want, I use a folder on my C:\ drive, for example. Check which folder regasm is in, as it differs for different .NET versions (this is for 4.5).

Build Events Code

"%WinDir%\Microsoft.NET\Framework\v4.0.30319\regasm" /tlb /unregister "$(TargetPath)"

echo UNREGISTERED

"%WinDir%\Microsoft.NET\Framework\v4.0.30319\regasm" /tlb /codebase "$(TargetPath)"

echo REGISTERED

xcopy "$(TargetPath)" "<folder where you want DLL to be copied to>" /D /Y

Batch File

ECHO OFF

ECHO We're first going to unregister, then register AmiCOMLib

ECHO UnRegistering AmiCOMLib

"C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm" "C:\ATS\AmiCOMLib.dll" /unregister 

ECHO UNREGISTERED

PAUSE

"C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm" "C:\ATS\AmiCOMLib.dll" /tlb /codebase

ECHO REGISTERED

PAUSE

Finally, I don't remember why, but I do it anyway, I sign the assembly. Under Properties > Signing > Check "Sign the Assembly". I just choose the default one, it looks like assemblyName.dll.snk.

To use it, in your AFL, first you'll create a static object (the compiler will complain about it, but using static object means it's created once and shared, as opposed to being created many times and eating up your memory - so you can ignore it, I've never had memory problems using it):

AmiComObj = CreateStaticObject("AmiCOMLib.AmiCOM"); //note format is namespace.class
quoteString = Name() + "," + close; //or, for example, what you have in your fputs
AmiComObj.SendQuotes(quoteString);

I personally use this method to check for certain conditions every tick, so I know it works for realtime applications.

Hope this gives you some ideas.

Good luck.

Sethmo