diff --git a/DllExport.bat b/DllExport.bat new file mode 100644 index 0000000..fc282e5 --- /dev/null +++ b/DllExport.bat @@ -0,0 +1,502 @@ +@echo off +:: Copyright (c) 2016-2020 Denis Kuzmin [ x-3F@outlook.com ] +:: https://github.com/3F/DllExport +if "%~1"=="/?" goto bq +set "aa=%~dpnx0" +set ab=%* +set ac=%* +if defined ab ( +if defined __p_call ( +set ac=%ac:^^=^% +) else ( +set ab=%ab:^=^^% +) +) +set wMgrArgs=%ac% +set ad=%ab:!=^!% +setlocal enableDelayedExpansion +set "ae=^" +set "ad=!ad:%%=%%%%!" +set "ad=!ad:&=%%ae%%&!" +set "af=1.7.1" +set "wAction=Configure" +set "ag=DllExport" +set "ah=tools/net.r_eg.DllExport.Wizard.targets" +set "ai=packages" +set "aj=https://www.nuget.org/api/v2/package/" +set "ak=build_info.txt" +set "al=!aa!" +set "wRootPath=!cd!" +set "am=" +set "an=" +set "ao=" +set "ap=" +set "aq=" +set "ar=" +set "as=" +set "at=" +set "au=" +set "av=" +set /a aw=0 +if not defined ab ( +if defined wAction goto br +goto bq +) +call :bs bk !ad! bl +goto bt +:bq +echo. +@echo .NET DllExport v1.7.1.6918+61defd6 +@echo Copyright (c) 2009-2015 Robert Giesecke +@echo Copyright (c) 2016-2020 Denis Kuzmin [ x-3F@outlook.com ] GitHub/3F +echo. +echo MIT License +@echo https://github.com/3F/DllExport +echo Based on hMSBuild, MvsSln, +GetNuTool: https://github.com/3F +echo. +@echo. +@echo Usage: DllExport [args to DllExport] [args to GetNuTool] [args to hMSBuild] +echo ------ +echo. +echo Arguments: +echo ---------- +echo -action {type} - Specified action for Wizard. Where {type}: +echo * Configure - To configure DllExport for specific projects. +echo * Update - To update pkg reference for already configured projects. +echo * Restore - To restore configured DllExport. +echo * Export - To export configured projects data. +echo * Recover - To re-configure projects via predefined/exported data. +echo * Unset - To unset all data from specified projects. +echo * Upgrade - Aggregates an Update action with additions for upgrading. +echo. +echo -sln-dir {path} - Path to directory with .sln files to be processed. +echo -sln-file {path} - Optional predefined .sln file to be processed. +echo -metalib {path} - Relative path to meta library. +echo -metacor {path} - Relative path to meta core library. +echo -dxp-target {path} - Relative path to entrypoint wrapper of the main core. +echo -dxp-version {num} - Specific version of DllExport. Where {num}: +echo * Versions: 1.6.6 ... +echo * Keywords: +echo `actual` - Unspecified local/latest remote version; +echo ( Only if you know what you are doing ) +echo. +echo -msb {path} - Full path to specific msbuild. +echo -hMSBuild {args} - Access to hMSBuild tool (packed) https://github.com/3F/hMSBuild +echo -packages {path} - A common directory for packages. +echo -server {url} - Url for searching remote packages. +echo -proxy {cfg} - To use proxy. The format: [usr[:pwd]@]host[:port] +echo -pkg-link {uri} - Direct link to package from the source via specified URI. +echo -force - Aggressive behavior, e.g. like removing pkg when updating. +echo -mgr-up - Updates this manager to version from '-dxp-version'. +echo -wz-target {path} - Relative path to entrypoint wrapper of the main wizard. +echo -pe-exp-list {module} - To list all available exports from PE32/PE32+ module. +echo -eng - Try to use english language for all build messages. +echo -GetNuTool {args} - Access to GetNuTool (integrated) https://github.com/3F/GetNuTool +echo -debug - To show additional information. +echo -version - Displays version for which (together with) it was compiled. +echo -build-info - Displays actual build information from selected DllExport. +echo -help - Displays this help. Aliases: -help -h +echo. +echo Flags: +echo ------ +echo __p_call - To use the call-type logic when invoking %~nx0 +echo. +echo Samples: +echo -------- +echo DllExport -action Configure -force -pkg-link http://host/v1.6.6.nupkg +echo DllExport -action Restore -sln-file "Conari.sln" +echo DllExport -proxy guest:1234@10.0.2.15:7428 -action Configure +echo. +echo DllExport -mgr-up -dxp-version 1.6.6 +echo DllExport -action Upgrade -dxp-version 1.6.6 +echo. +echo DllExport -GetNuTool /p:ngpackages="Conari;regXwild" +echo DllExport -pe-exp-list bin\Debug\regXwild.dll +goto bu +:bt +set /a ax=0 +:bv +set ay=!bk[%ax%]! +if [!ay!]==[-help] ( goto bq ) else if [!ay!]==[-h] ( goto bq ) else if [!ay!]==[-?] ( goto bq ) +if [!ay!]==[-debug] ( +set am=1 +goto bw +) else if [!ay!]==[-action] ( set /a "ax+=1" & call :bx bk[!ax!] v +set wAction=!v! +for %%g in (Restore, Configure, Update, Export, Recover, Unset, Upgrade, Default) do ( +if "!v!"=="%%g" goto bw +) +echo Unknown -action !v! +exit/B 1 +) else if [!ay!]==[-sln-dir] ( set /a "ax+=1" & call :bx bk[!ax!] v +set wSlnDir=!v! +goto bw +) else if [!ay!]==[-sln-file] ( set /a "ax+=1" & call :bx bk[!ax!] v +set wSlnFile=!v! +goto bw +) else if [!ay!]==[-metalib] ( set /a "ax+=1" & call :bx bk[!ax!] v +set wMetaLib=!v! +goto bw +) else if [!ay!]==[-metacor] ( set /a "ax+=1" & call :bx bk[!ax!] v +set wMetaCor=!v! +goto bw +) else if [!ay!]==[-dxp-target] ( set /a "ax+=1" & call :bx bk[!ax!] v +set wDxpTarget=!v! +goto bw +) else if [!ay!]==[-dxp-version] ( set /a "ax+=1" & call :bx bk[!ax!] v +set af=!v! +goto bw +) else if [!ay!]==[-msb] ( set /a "ax+=1" & call :bx bk[!ax!] v +set ao=!v! +goto bw +) else if [!ay!]==[-packages] ( set /a "ax+=1" & call :bx bk[!ax!] v +set ai=!v! +goto bw +) else if [!ay!]==[-server] ( set /a "ax+=1" & call :bx bk[!ax!] v +set aj=!v! +goto bw +) else if [!ay!]==[-proxy] ( set /a "ax+=1" & call :bx bk[!ax!] v +set at=!v! +set wProxy=!v! +goto bw +) else if [!ay!]==[-pkg-link] ( set /a "ax+=1" & call :bx bk[!ax!] v +set ap=!v! +goto bw +) else if [!ay!]==[-force] ( +set ar=1 +goto bw +) else if [!ay!]==[-mgr-up] ( +set as=1 +goto bw +) else if [!ay!]==[-wz-target] ( set /a "ax+=1" & call :bx bk[!ax!] v +set ah=!v! +goto bw +) else if [!ay!]==[-pe-exp-list] ( set /a "ax+=1" & call :bx bk[!ax!] v +set aq=!v! +goto bw +) else if [!ay!]==[-eng] ( +chcp 437 >nul +goto bw +) else if [!ay!]==[-GetNuTool] ( +call :by -GetNuTool 10 +set /a aw=!ERRORLEVEL! & goto bu +) else if [!ay!]==[-hMSBuild] ( +set av=1 & goto br +) else if [!ay!]==[-version] ( +@echo v1.7.1.6918+61defd6 %__dxp_pv% +goto bu +) else if [!ay!]==[-build-info] ( +set an=1 +goto bw +) else if [!ay!]==[-tests] ( set /a "ax+=1" & call :bx bk[!ax!] v +set au=!v! +goto bw +) else ( +echo Incorrect key: !ay! +set /a aw=1 +goto bu +) +:bw +set /a "ax+=1" & if %ax% LSS !bl! goto bv +:br +call :bz "dxpName = " ag +call :bz "dxpVersion = " af +call :bz "-sln-dir = " wSlnDir +call :bz "-sln-file = " wSlnFile +call :bz "-metalib = " wMetaLib +call :bz "-metacor = " wMetaCor +call :bz "-dxp-target = " wDxpTarget +call :bz "-wz-target = " ah +if defined af ( +if "!af!"=="actual" ( +set "af=" +) +) +set wPkgVer=!af! +if z%wAction%==zUpgrade ( +call :bz "Upgrade is on" +set as=1 +set ar=1 +) +call :b0 ai +set "ai=!ai!\\" +set "az=!ag!" +set "wPkgPath=!ai!!ag!" +if defined af ( +set "az=!az!/!af!" +set "wPkgPath=!wPkgPath!.!af!" +) +if defined ar ( +if exist "!wPkgPath!" ( +call :bz "Removing old version before continue. '-force' key rule. " wPkgPath +rmdir /S/Q "!wPkgPath!" +) +) +set a0="!wPkgPath!\\!ah!" +call :bz "wPkgPath = " wPkgPath +if not exist !a0! ( +if exist "!wPkgPath!" ( +call :bz "Trying to replace obsolete version ... " wPkgPath +rmdir /S/Q "!wPkgPath!" +) +call :bz "-pkg-link = " ap +call :bz "-server = " aj +if defined ap ( +set aj=!ap! +if "!aj::=!"=="!aj!" ( +set aj=!cd!/!aj! +) +if "!wPkgPath::=!"=="!wPkgPath!" ( +set "a1=../" +) +set "az=:!a1!!wPkgPath!|" +) +if defined ao ( +set a2=-msbuild "!ao!" +) +set a3=!a2! /p:ngserver="!aj!" /p:ngpackages="!az!" /p:ngpath="!ai!" /p:proxycfg="!at! " +call :bz "GetNuTool call: " a3 +if defined am ( +call :b1 !a3! +) else ( +call :b1 !a3! >nul +) +) +if defined av ( +call :by -hMSBuild 9 +set /a aw=!ERRORLEVEL! & goto bu +) +if defined aq ( +"!wPkgPath!\\tools\\PeViewer.exe" -list -pemodule "!aq!" +set /a aw=%ERRORLEVEL% +goto bu +) +if defined an ( +call :bz "buildInfo = " wPkgPath ak +if not exist "!wPkgPath!\\!ak!" ( +echo information about build is not available. +set /a aw=2 +goto bu +) +type "!wPkgPath!\\!ak!" +goto bu +) +if not exist !a0! ( +echo Something went wrong. Try to use another keys. +set /a aw=2 +goto bu +) +call :bz "wRootPath = " wRootPath +call :bz "wAction = " wAction +call :bz "wMgrArgs = " wMgrArgs +if defined ao ( +call :bz "Use specific MSBuild tools: " ao +set a4="!ao!" +goto b2 +) +call :b3 bm & set a4="!bm!" +if "!ERRORLEVEL!"=="0" goto b2 +echo MSBuild tools was not found. Try with `-msb` key. +set /a aw=2 +goto bu +:b2 +if not defined a4 ( +echo Something went wrong. Use `-debug` key for details. +set /a aw=2 +goto bu +) +if not defined au ( +if not defined ao if defined wPkgPath ( +set a4="!wPkgPath!\\hMSBuild" +for /f "tokens=*" %%i in ('!a4! -version') do set a5=%%i +call :b4 !a5! bn +call :bz "hMSBuild -v" a5 bn +if !bn! GEQ 230 ( +call :bz "2.3+" +set a4=!a4! -vsw-as "-requiresAny -requires Microsoft.NetCore.Component.SDK Microsoft.Net.Core.Component.SDK -products * -latest -prerelease" +) +) +call :bz "Target: " a4 a0 +call !a4! /nologo /v:m /m:4 !a0! +) +:bu +if defined au ( +echo Running Tests ... "!au!" +call :b3 bo +"!bo!" /nologo /v:m /m:4 "!au!" +exit/B 0 +) +if defined as ( +(copy /B/Y "!wPkgPath!\\DllExport.bat" "!al!" > nul) && ( echo Manager has been updated. & exit/B 0 ) || ( (echo -mgr-up failed:!aw! 1>&2) & exit/B 1 ) +) +exit/B !aw! +:b4 +set a6=%~1 +for /f "tokens=1,2 delims=." %%a in ("!a6!") do ( +set _=%%b & set /a _*=10 & set /a %2=%%a!_! +) +exit/B 0 +:by +set ay=%~1 +set /a a7=%~2 +call :bz "accessing to !ay! ..." +for /L %%p IN (0,1,8181) DO ( +if "!ad:~%%p,%a7%!"=="!ay!" ( +set a8=!ad:~%%p! +set a9=!a8:~%a7%! +if defined av ( +call "!wPkgPath!\\hMSBuild" !a9! +) else ( +call :b1 !a9! +) +exit/B !ERRORLEVEL! +) +) +call :bz "!ay! is corrupted: " ad +exit/B 1 +:b3 +call :bz "Searching from .NET Framework - .NET 4.0, ..." +for %%v in (4.0, 3.5, 2.0) do ( +call :b5 %%v Y & if defined Y ( +set %1=!Y! +exit/B 0 +) +) +call :bz "msb -netfx: not found" +set "%1=" +exit/B 2 +:b5 +call :bz "check %1" +for /F "usebackq tokens=2* skip=2" %%a in ( +`reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\%1" /v MSBuildToolsPath 2^> nul` +) do if exist %%b ( +set a_=%%~b +call :bz ":msbfound " a_ +call :b6 a_ bp +set %2=!bp! +exit/B 0 +) +set "%2=" +exit/B 0 +:b6 +set %2=!%~1!\MSBuild.exe +exit/B 0 +:bz +if defined am ( +set ba=%1 +set ba=!ba:~0,-1! +set ba=!ba:~1! +echo.[%TIME% ] !ba! !%2! !%3! +) +exit/B 0 +:b0 +call :b7 %1 +call :b8 %1 +exit/B 0 +:b7 +call :b9 %1 "-=1" +exit/B 0 +:b8 +call :b9 %1 "+=1" +exit/B 0 +:b9 +set bb=z!%1!z +if "%~2"=="-=1" (set "bc=1") else (set "bc=") +if defined bc ( +set /a "i=-2" +) else ( +set /a "i=1" +) +:b_ +if "!bb:~%i%,1!"==" " ( +set /a "i%~2" +goto b_ +) +if defined bc set /a "i+=1" +if defined bc ( +set "%1=!bb:~1,%i%!" +) else ( +set "%1=!bb:~%i%,-1!" +) +exit/B 0 +:bs +set "bd=%~1" +set /a ax=-1 +:ca +set /a ax+=1 +set %bd%[!ax!]=%~2 +shift & if not "%~3"=="" goto ca +set /a ax-=1 +set %1=!ax! +exit/B 0 +:bx +set %2=!%1! +exit/B 0 +:b1 +setlocal disableDelayedExpansion +@echo off +:: GetNuTool - Executable version +:: Copyright (c) 2015-2018,2020 Denis Kuzmin [ x-3F@outlook.com ] +:: https://github.com/3F/GetNuTool +set be=gnt.core +set bf="%temp%\%random%%random%%be%" +if "%~1"=="-unpack" goto cb +set bg=%* +if defined __p_call if defined bg set bg=%bg:^^=^% +set bh=%__p_msb% +if defined bh goto cc +if "%~1"=="-msbuild" goto cd +for %%v in (4.0, 14.0, 12.0, 3.5, 2.0) do ( +for /F "usebackq tokens=2* skip=2" %%a in ( +`reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\%%v" /v MSBuildToolsPath 2^> nul` +) do if exist %%b ( +set bh="%%~b\MSBuild.exe" +goto cc +) +) +echo MSBuild was not found. Try -msbuild "fullpath" args 1>&2 +exit/B 2 +:cd +shift +set bh=%1 +shift +set bi=%bg:!= #__b_ECL## % +setlocal enableDelayedExpansion +set bi=!bi:%%=%%%%! +:ce +for /F "tokens=1* delims==" %%a in ("!bi!") do ( +if "%%~b"=="" ( +call :cf !bi! +exit/B %ERRORLEVEL% +) +set bi=%%a #__b_EQ## %%b +) +goto ce +:cf +shift & shift +set "bg=" +:cg +set bg=!bg! %1 +shift & if not "%~2"=="" goto cg +set bg=!bg: #__b_EQ## ==! +setlocal disableDelayedExpansion +set bg=%bg: #__b_ECL## =!% +:cc +call :ch +call %bh% %bf% /nologo /p:wpath="%cd%/" /v:m /m:4 %bg% +set "bh=" +set bj=%ERRORLEVEL% +del /Q/F %bf% +exit/B %bj% +:cb +set bf="%cd%\%be%" +echo Generating minified version in %bf% ... +:ch +%bf% +set a=PropertyGroup&set b=Condition&set c=ngpackages&set d=Target&set e=DependsOnTargets&set f=TaskCoreDllPath&set g=MSBuildToolsPath&set h=UsingTask&set i=CodeTaskFactory&set j=ParameterGroup&set k=Reference&set l=Include&set m=System&set n=Using&set o=Namespace&set p=IsNullOrEmpty&set q=return&set r=string&set s=delegate&set t=foreach&set u=WriteLine&set v=Combine&set w=Console.WriteLine&set x=Directory&set y=GetNuTool&set z=StringComparison&set _=EXT_NUSPEC +^ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + This plug-in requires your acceptance of the following license terms for use, both the terms of the GNU Lesser General Public License, version 3.0, and the addendum to these license terms. Please read the following terms carefully. + + + "COMMONS CLAUSE" LICENSE CONDITION, version 1.0 + +The Software is provided to you by the Licensor under the License, as defined below, subject to the following condition. + +Without limiting other conditions in the License, the grant of rights under the License will not include, and the License does not grant to you, the right to Sell the Software. + +For purposes of the foregoing, “Sell” means practicing any or all of the rights granted to you under the License to provide to third parties, for a fee or other consideration (including without limitation fees for hosting or consulting/ support services related to the Software), a product or service whose value derives, entirely or substantially, from the functionality of the Software. Any license notice or attribution required by the License must also include this Commons Clause License Condition notice. + +SOFTWARE: TD Ameritrade Zorro Broker Plug-in +LICENSE: GNU Lesser General Public License +LICENSOR: Clyde W. Ford + +GNU LESSER GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. + +0. Additional Definitions. + +As used herein, “this License” refers to version 3 of the GNU Lesser General Public License, and the “GNU GPL” refers to version 3 of the GNU General Public License. + +“The Library” refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. + +An “Application” is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. + +A “Combined Work” is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the “Linked Version”. + +The “Minimal Corresponding Source” for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. + +The “Corresponding Application Code” for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. + +1. Exception to Section 3 of the GNU GPL. + +You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. + +2. Conveying Modified Versions. + +If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: + +a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or +b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. +3. Object Code Incorporating Material from Library Header Files. + +The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: + +a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. +b) Accompany the object code with a copy of the GNU GPL and this license document. +4. Combined Works. + +You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: + +a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. +b) Accompany the Combined Work with a copy of the GNU GPL and this license document. +c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. +d) Do one of the following: +0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. +1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. +e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) +5. Combined Libraries. + +You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: + +a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. +b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. +6. Revised Versions of the GNU Lesser General Public License. + +The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. + +7. You, furthermore, agree to the following terms, not included in the "Commons Clause" License Condition, version 1.0, or GNU Lesser Generaly Pubilc License 3.0 as described above: +a) The DEVELOPER of this software is Clyde W. Ford. The USER of this software is you. SOFTWARE refers to this product, commonly known as a "TD Ameritrade Broker Plug-In for the Zorro Trading Engine" or "Broker Plug-In." +b) SOFTWARE is offered on an AS IS basis with no claims or warranties, implied or expressed, as to its fitness, completeness or competency to perform any given task, or fulfill any given purpose. +c) SOFTWARE is used AT THE RISK OF USER. +d) SOFTWARE may be employed in financial transactions that hold considerable risk for USER. +e) USER assumes full responsibilty for the risk of any financial transactions entered into through the use of SOFTWARE. +f) USER indemnifies and holds harmless the developer, and his assignees, for any damages, financial and/or otherwise, that arise from the use of SOFTWARE. +g) SOFTWARE interoperates with other software from TD Ameritrade, Inc. (commonly known as "TD Ameritrade") and oP group Germany GmbH (commonly known as "Zorro Trading Engine"). +h) TD Ameritrade and oP group Germany GmbH may modiy their respective software products in such a way that renders SOFTWARE inoperable with them. +i) USER indemnifies and holds harmless the developer, and his assignees, for any damages, financial and/or otherwise, that arise from modifications to software produced by TD Ameritrade or oP group Germany GmbH. +h) SOFTWARE AS IS may contain errors, inaccuracies, and other defects. +i) USER indemnifies and holds harmless the developer, and his assignees, for any damages, financial and/or otherwise, that arise from errors, inaccuracies, and other defects in the SOFTWARE. +j) DEVELOPER makes no claims or warranties, implied or expressed, that he will at any time now, or in the future, fix, address, or otherwise cause to correct any errors, inaccuracies or other defects in the SOFTWARE. + + + + + AAABAAEAICAQdAAAAADoAgAAFgAAACgAAAAgAAAAQAAAAAEABAAAAAAAgAIAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAACAAACAAAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD//wD/AAAA/wD/AP// + AAD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEREREREREREAAAAAAAAAAJmZ + mZmZmZmZEAAAAAAAAACZkRERGZmZmQAAAAAAAAAAGZEAAAAAAAAAAAAAAAAAAAmZEAAAAAAAAAAAAAAA + AAAAmZAAAAAAAAAAAAAAAAACIBmRAAACIiIAAAAAAAAAJ3IBmRAAJ3d3dzIAAAAAACdyAJmQACd3N3d3 + MAAAAAAncgAJkQAncAAAJ3MAAAAAJ3IAAZkQJ3AAAAJ3IAAAACdyAAAZkCdwAAAAN3AAAAAncgAACZk3 + cAAAACdwAAAAJ3IAAAGZl3AAAAAHcgAAACdyAAAAGZkwAAAAB3IAAAAncgAAAAGZkAAAAAdyAAAAJ3IA + AAAAGZkQAAAHcwAAACcxEREQADmZkQAAB3MAAAAnmZmZmZmZmZkQAAdyAAAAJ5mZmZmZmZmZEAAHcgAA + ACdxAAAAADMxAAAAB3IAAAAncgAAAAAncAAAACdwAAAAJ3IAAAAAJ3AAAAA3cAAAACdyAAAAACdwAAAC + dyAAAAAncgAAAAAncAACN3MAd3d3d3d3d3cAJ3d3d3cgAHd3d3d3d3d3ACd3d3cgAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////gAD//7////+f + ////3////+/////3///+d+B//nvnj/595+f+f+fz/n7n+/5/Z/n+f6f5/n/H/f5/5/3+f+f9/n/j/f5/ + 4f3+f+f9/n/n+f5/5/n+f+f7/n/n8/5/5+eAAecfgAHgf////////////////w== + + + \ No newline at end of file diff --git a/TDAmeritradeZorro/Classes/LogHelper.cs b/TDAmeritradeZorro/Classes/LogHelper.cs new file mode 100644 index 0000000..2734765 --- /dev/null +++ b/TDAmeritradeZorro/Classes/LogHelper.cs @@ -0,0 +1,330 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace TDAmeritradeZorro.Classes +{ + //************************************************************************* + // Class: LogHelper + // + /// + /// A class to assist in showing log messages in the Zorro window and the + /// log file. + /// + //************************************************************************* + public static class LogHelper + { + //********************************************************************* + // Member: LINE_FORMATSTR + // + /// + /// Format string for log entry with line numbers only. + /// + //********************************************************************* + private readonly static string LINE_FORMATSTR = "[%sourcefile.%method (%linenumber)] %level - %message%newline"; + + //********************************************************************* + // Member: TIME_FORMATSTR + // + /// + /// Format string for log entry with time only. + /// + //********************************************************************* + private readonly static string TIME_FORMATSTR = "%timestamp(yyyy-MM-dd HH:mm:ss.ff) %level - %message%newline"; + + //********************************************************************* + // Member: TIME_LINE_FORMATSTR + // + /// + /// Format string for log entry with both time and line numbers + /// + //********************************************************************* + private readonly static string TIME_LINE_FORMATSTR = "%timestamp(yyyy-MM-dd HH:mm:ss.ff) [%sourcefile.%method (%linenumber)] %level - %message%newline"; + + // Newline character + private readonly static string NEWLINE = "\r\n"; + + // Dictionary with string tokens and format token position + private readonly static Dictionary fmtDictionary = new Dictionary + { + {"%timestamp", "{0}" }, + {"%sourcefile", "{1}" }, + {"%method", "{2}" }, + {"%linenumber", "{3}" }, + {"%level", "{4}" }, + {"%message", "{5}" }, + }; + + // Regex to get the date format string + private static Regex reDate = new Regex(@"%timestamp\((.*?)\)"); + + // Regex for finding a numbered filename + private static Regex reNum = new Regex(@"\((.*)\)"); + + // The date format string + private static string dateFmt; + + //********************************************************************* + // Method: ParseFormat + // + /// + /// Parse the format string for a log entry. + /// + /// + /// + /// A string format template that can be used to populate the string + /// with values either passed with the call or inherent to OS. + /// + //********************************************************************* + private static string + ParseFormat + ( + bool timeStamp, + bool lineNumbers + ) + { + // Get the format string + string fmtString = TIME_LINE_FORMATSTR; + + // Make sure correct format string is obtained + if (timeStamp & !lineNumbers) fmtString = TIME_FORMATSTR; + else if (lineNumbers & !timeStamp) fmtString = LINE_FORMATSTR; + + // Work around memory leak in Regex + Regex.CacheSize = 0; + + // Look for the date format string + Match m = reDate.Match(fmtString); + + // Remove the date format string, if found + if (m.Success) + fmtString = fmtString.Replace(m.Groups[0].Value, "%timestamp"); + + // Replace the newline tokens + fmtString = fmtString.Replace("%newline", NEWLINE); + + // Iterate through the format dictionary, and... + foreach (string key in fmtDictionary.Keys) + + // Replace the key with its value in the format string + fmtString = fmtString.Replace(key, $"{fmtDictionary[key]}"); + + // Return the format string + return fmtString; + } + + //********************************************************************* + // Method: Log + // + /// + /// Log a message. + /// + /// + /// + /// Text of message to log. + /// + /// + /// + /// Autopopulated with the source file from which this method was + /// called. + /// + /// + /// + /// Autopopulated with the method withing the source file from which + /// this method was called. + /// + /// + /// + /// Autopopulated with the line number of the source file from which + /// this method was called. + /// + /// + /// + /// Based on the current verbosity level, determines if a message is + /// shown and whether it is display in the Zorro Window, the log file, + /// or both. + /// + //********************************************************************* + public static void + Log + ( + LogLevel level, + string text, + [CallerFilePath] string file = "", + [CallerMemberName] string method = "", + [CallerLineNumber] int line = 0 + ) + { + // Basic verbosity flags + bool byPass = false; + bool logOnly = false; + + // Determine if the verbosity level includes flags for adding time + // stamp and line number to messages printed to log. + bool timeStamp = TDAmerAPI.verbosityLevel.HasFlag(Verbosity.TimeStamp); + bool lineNumbers = TDAmerAPI.verbosityLevel.HasFlag(Verbosity.LineNumbers); + + // Remove the timestamp and line number flags from the verbosity + // level of the plug-in + Verbosity vLevel = TDAmerAPI.verbosityLevel & ~Verbosity.TimeStamp; + vLevel &= ~Verbosity.LineNumbers; + + // Switch based on the current verbosity level + switch (vLevel) + { + // Only message level severe to show in Zorro Window and file + // Message level Warning and Error show in log file only. + // Anything else does not show at all + case Verbosity.Basic: + switch (level) + { + case LogLevel.Error: + case LogLevel.Warning: + logOnly = true; + break; + + case LogLevel.Info: + case LogLevel.Caution: + byPass = true; + break; + } + break; + + // Message levels Critical and Error to show in Zorro Window + // Message levels Warning and Cautionto show in log file only + // Anything else does not show at all + case Verbosity.Intermediate: + switch (level) + { + case LogLevel.Caution: + case LogLevel.Warning: + logOnly = true; + break; + + case LogLevel.Info: + byPass = true; + break; + } + break; + + // Message levels Critical, Error, and Warning to show in + // Zorro Window. Message levels Caution and Info to show in + // log file only. + case Verbosity.Advanced: + switch (level) + { + case LogLevel.Caution: + case LogLevel.Info: + logOnly = true; + break; + } + break; + + // Message levels other than Info to show in Zorro Window. + // Info level in log only + case Verbosity.Extensive: + switch (level) + { + case LogLevel.Info: + logOnly = true; + break; + } + break; + + case Verbosity.Extreme: + logOnly = true; + break; + } + + // Is message to be bypassed at this VERBOSITY level? + if (!byPass) + { + // NO: Is message for log file only + if (logOnly) + { + // YES: Using a timestamp or a line number? + if (timeStamp || lineNumbers) + { + // YES: Write the msg to log file with these prepended + text = "#" + string.Format( + + // The log entry template + ParseFormat(timeStamp, lineNumbers), + + // The current timestamp (0) + DateTime.Now.ToString(dateFmt), + + // The source file (1) + Path.GetFileNameWithoutExtension(file), + + // The method (2) + method, + + // The line number (3) + line, + + // The severity level + level.ToString().ToUpper(), + + // The message + text + ); + + } + } + + // SHow the message + TDAmerAPI.BrokerError(text); + } + } + + //********************************************************************* + // Method: Log (Overload) + // + /// + /// Show a log message. + /// + /// + /// + /// The message string to show. + /// + /// + /// + /// Autopopulated with the source file from which this method was + /// called. + /// + /// + /// + /// Autopopulated with the method withing the source file from which + /// this method was called. + /// + /// + /// + /// Autopopulated with the line number of the source file from which + /// this method was called. + /// + /// + /// + /// Overload of log method for showing message at default setting of + /// LogLevel = Info. + /// + //********************************************************************* + public static void + Log + ( + string text, + [CallerFilePath] string file = "", + [CallerMemberName] string method = "", + [CallerLineNumber] int line = 0 + ) + { + // Call log method with info level and other data set + LogHelper.Log(LogLevel.Info, text, file, method, line); + } + } +} diff --git a/TDAmeritradeZorro/Classes/MutualFundInfo.cs b/TDAmeritradeZorro/Classes/MutualFundInfo.cs new file mode 100644 index 0000000..b7b7179 --- /dev/null +++ b/TDAmeritradeZorro/Classes/MutualFundInfo.cs @@ -0,0 +1,319 @@ +//***************************************************************************** +// File: MutualFundInfo.cs +// +// Author: Clyde W. Ford +// +// Date: April 29, 2020 +// +// Description: Retrieve basic information about a mutual fund. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using TDAmeritradeZorro.Utilities; + +namespace TDAmeritradeZorro.Classes +{ + //************************************************************************* + // Class: MutualFundFinder + // + /// + /// A class to hold basic information about a mutual. + /// + //************************************************************************* + + public class MutualFundInfo + { + #region CLASS MEMBERS + //********************************************************************* + // Member: baseUrl + // + /// + /// The url template used to look-up information about a mutual fund. + /// + //********************************************************************* + // This is the base URL. Currently using data from MaxFunds.Com + private static readonly string baseUrl = "http://www.maxfunds.com/funds/data.php?ticker={0}"; + + //********************************************************************* + // Members: reTopData, liData, spanData, tdData + // + /// + /// Regex strings used to find HTML elements + /// + /// + /// + /// reTopData = to find the "topData"
element. + /// liData = to find all
  • elements. + /// spanData = to find all elements. + /// tdData = to find all elements. + /// + //********************************************************************* + private static Regex reTopData = new Regex("
    (.*?)div>"); + private static Regex liData = new Regex("
  • (.*?)
  • "); + private static Regex spanData = new Regex("(.*?)"); + private static Regex tdData = new Regex("(.*?)"); + + #endregion CLASS MEMBERS + + #region CLASS PROPERTIES + //********************************************************************* + // Property: Symbol + // + /// + /// The mutual fund symbol + /// + //********************************************************************* + public string Symbol { get; set; } + + //********************************************************************* + // Property: Type + // + /// + /// The mutual fund type (NO LOAD, LOAD, etc). + /// + //********************************************************************* + public string Type { get; set; } + + //********************************************************************* + // Property: MinimumInvestment + // + /// + /// The initial minimum investment in the fund. + /// + //********************************************************************* + public double MinimumInvestment { get; set; } + + //********************************************************************* + // Property: InitialFees + // + /// + /// The initial fees for the fund. + /// + //********************************************************************* + public double InitialFees { get; set; } + + //********************************************************************* + // Property: DeferredFees + // + /// + /// The deferred fees for the fund. + /// + //********************************************************************* + public double DeferredFees { get; set; } + + //********************************************************************* + // Property: RedemptionFees + // + /// + /// The redemption fees for the fund. + /// + //********************************************************************* + public double RedemptionFees { get; set; } + #endregion CLASS MEMBERS + + #region CONSTRUCTOR + //********************************************************************* + // Property: MutualFundInfo + // + /// + /// The class constructor. + /// + //********************************************************************* + public MutualFundInfo + () + { + + } + #endregion CONSTRUCTOR + + #region PUBLIC STATIC METHODS + //********************************************************************* + // Member: GutFundInfo + // + /// + /// Get information about a mutual fund + /// + /// + /// + /// The stock ticker symbol for the mutual fund. + /// + //********************************************************************* + public static MutualFundInfo + GetFundInfo + ( + string symbol + ) + { + // Method members + MutualFundInfo mfi = new MutualFundInfo(); + string[] liInfo = new string[4]; + string result; + Match m; + bool found = false; + MatchCollection mc; + + // Read the information page for this fund + result = Helper.GetWebPage(string.Format(baseUrl, symbol)); + result = result.Replace("\r", "").Replace("\n", "").Replace("\t", ""); + + // Isolate the top line data on the fund + m = reTopData.Match(result); + + // Was a match found? + if (m.Success) + { + // YES: Get the
  • data in this match + mc = liData.Matches(m.Groups[1].Value); + + //
  • data found? + if (mc.Count == 4) + { + // YES: Create a new mutual fund object + mfi = new MutualFundInfo(); + + // Iterate through the
  • elements and get the data + for (int i = 0; i < mc.Count; ++i) + { + m = spanData.Match(mc[i].Value); + liInfo[i] = m.Groups[1].Value; + if (!string.IsNullOrEmpty(m.Groups[1].Value)) found = true; + } + + // Was any data found + if (found) + { + // Add the
  • info to the object + + // Type of fund + mfi.Type = liInfo[1]; + + // Minimum investment + mfi.MinimumInvestment = GetNumber(liInfo[2]); + + // If we got here, then add the ticker symbol + mfi.Symbol = symbol; + + // Look for the costs and fees + mc = tdData.Matches(result); + + // Were costs and fees found? + if (mc.Count > 0) + { + // MAYBE: Iterate through all the TD elements + for (int i = 0; i < mc.Count; ++i) + { + // Is this the TD element for "Initial Fees" + if (mc[i].Groups[0].Value.Contains("Initial")) + { + // YES: Get the value + mfi.InitialFees = GetNumber(mc[i + 1].Groups[2].Value); + } + + // Is this the TD element for "Deferred Fees" + if (mc[i].Groups[0].Value.Contains("Deferred")) + { + // YES: Get the value + mfi.DeferredFees = GetNumber(mc[i + 1].Groups[2].Value); + } + + if (mc[i].Groups[0].Value.Contains("Redemption")) + { + // YES: Get the value + mfi.RedemptionFees = GetNumber(mc[i + 1].Groups[2].Value); + } + } + } + } + } + } + + // Return the fund info class object + return mfi; + } + #endregion PUBLIC STATIC METHODS + + #region PRIVATE STATIC METHODS + //********************************************************************* + // Method: GetNumber + // + /// + /// Get a number from a string that has alphabetic multipliers. + /// + /// + /// + /// The number string. + /// + /// + /// + /// A double value representing the number. + /// + //********************************************************************* + private static double + GetNumber + ( + string field + ) + { + // Method membors + double dVal; + string dStr; + string multiplier; + + // Replace the dollar sign + field = field.Replace("$", "").Trim(); + + // Get the field without the multiplier + dStr = Regex.Replace(field, "[A-Za-z]", "").Trim(); + + // Get the multiplier by removing all numbers and periods and + // commas from string + multiplier = Regex.Replace(field, "[0-9.,]", "").Trim(); + + // Attempt to convert the number + if (Double.TryParse(dStr, out dVal)) + { + // Use the mutiplier + switch(multiplier) + { + case "K": + dVal *= 1000.0; + break; + + case "M": + dVal *= 1000000.0; + break; + + default: + break; + } + } + else + { + dVal = 0.0; + } + + // Return the double value + return dVal; + } + #endregion PRIVATE STATIC METHODS + } +} diff --git a/TDAmeritradeZorro/Classes/OptionAsset.cs b/TDAmeritradeZorro/Classes/OptionAsset.cs new file mode 100644 index 0000000..ef36c02 --- /dev/null +++ b/TDAmeritradeZorro/Classes/OptionAsset.cs @@ -0,0 +1,81 @@ +//***************************************************************************** +// File: OptionAsset.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: Forex asset class. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System; +namespace TDAmeritradeZorro.Classes +{ + //************************************************************************* + // Class: ForexAsset + // + /// + /// The Option asset add-on to the TDAsset class + /// + //************************************************************************* + public class OptionAsset + { + //********************************************************************* + // Property: ExpirationDate + // + /// + /// Expiration date for on OPTION contract. + /// + //********************************************************************* + public DateTime ExpirationDate { get; set; } + + //********************************************************************* + // Property: StrikePrice + // + /// + /// Strike (contract) price for an OPTION contract. + /// + //********************************************************************* + public double StrikePrice { get; set; } + + //********************************************************************* + // Property: PutCallType + // + /// + /// "P" = PUT; "C" = CALL + /// + //********************************************************************* + public string PutCallType { get; set; } + + //********************************************************************* + // Property: OptionAsset + // + /// + /// Class constructor + /// + //********************************************************************* + public OptionAsset() + { + ExpirationDate = DateTime.MinValue; + StrikePrice = 0.0; + PutCallType = string.Empty; + } + } +} diff --git a/TDAmeritradeZorro/Classes/OrderSubmission.cs b/TDAmeritradeZorro/Classes/OrderSubmission.cs new file mode 100644 index 0000000..0fa3968 --- /dev/null +++ b/TDAmeritradeZorro/Classes/OrderSubmission.cs @@ -0,0 +1,929 @@ +//***************************************************************************** +// File: OrderSubmission.cs +// +// Author: Clyde W. Ford +// +// Date: May 8, 2020 +// +// Description: Store order information and create order JSON. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System; +using System.Collections.Generic; +using TDAmeritradeZorro.Classes.TDA; +using TDAmeritradeZorro.Utilities; + +namespace TDAmeritradeZorro.Classes +{ + //************************************************************************* + // Class: OrderSubmission + // + /// + /// A class that encapsulates the information needed to suubit an order to + /// // the TD Ameritrade API. + /// + //************************************************************************* + public class OrderSubmission + { + #region CLASS MEMBERS + //********************************************************************* + // Member: comboOrders + // + /// + /// A list of combo OPTION orders waiting to be executed as a single + /// order. + /// + //********************************************************************* + private static List comboOrders; + + //********************************************************************* + // Member: stockOrderTpl + // + /// + /// The template for a stock order (BUY or SELL) with no STOP + /// + //********************************************************************* + private static string stockOrderTpl = + "{{" + + // MARKET or LIMIT + @"""orderType"": ""{0}""," + + + // NORMAL, AM, PM, SEAMLESS + @"""session"": ""{1}""," + + + // DAY, GOOD_TILL_CANCEL, FILL_OR_KILL + @"""duration"": ""{2}""," + + + // ORDER STRATEGY + @"""orderStrategyType"": ""{3}""," + + + // The ORDER LEG collection (only one for plugin) + @"""orderLegCollection"": [" + + "{{" + + // BUY, SELL, BUY_TO_COVER, SELL_SHORT, BUY_TO_OPEN, + // BUY_TO_CLOSE, SELL_TO_OPEN, SELL_TO_CLOSE, EXCHANGE + @"""instruction"": ""{4}""," + + + // Order type: EQUITY, QPTION, INDEX, MUTUAL_FUND, + // CASH_EQUIVALENT, FIXED_INCOME, CURRENCY + @"""orderLegType"": ""{7}""," + + + // Order quantity, + @"""quantity"": {5}," + + + // The Instrument + @"""instrument"": {{" + + + // The asset symbol + @"""symbol"": ""{6}""," + + + // Asset type + @"""assetType"": ""{7}""" + + "}}" + + "}}" + + "]" + + "}}"; + + //********************************************************************* + // Member: stockOrderTpl + // + /// + /// The template for a stock order (BUY or SELL) with no STOP + /// + //********************************************************************* + private static string optionsOrderTpl = + "{{" + + // ORDER STRATEGY: MARKET or LIMIT + @"""orderStrategyType"": ""{3}""," + + + // Order Strategy Type: MARKET or LIMIT + @"""orderType"": ""{0}""," + + + // The ORDER LEG collection (only one for plugin) + @"""orderLegCollection"": [" + + "{{" + + // The Instrument + @"""instrument"": {{" + + + // Asset type + @"""assetType"": ""{7}""," + + + // The asset symbol + @"""symbol"": ""{6}""" + + + "}}," + + + // BUY, SELL, BUY_TO_COVER, SELL_SHORT, BUY_TO_OPEN, + // BUY_TO_CLOSE, SELL_TO_OPEN, SELL_TO_CLOSE, EXCHANGE + @"""instruction"": ""{4}""," + + + // Order quantity, + @"""quantity"": {5}" + + + "}}" + + + "]," + + + // Complex order strategy type + //@"""complexOrderStrategyType"": ""NONE""," + + + // DAY, GOOD_TILL_CANCEL, FILL_OR_KILL + @"""duration"": ""{2}""," + + + // NORMAL, AM, PM, SEAMLESS + @"""session"": ""{1}""" + + + "}}"; + + //********************************************************************* + // Member: orderLegTpl + // + /// + /// The template for a single order leg, used for combo OPTION orders. + /// + //********************************************************************* + private static string orderLegTpl = + "{{" + + // The Instrument + @"""instrument"": {{" + + + // Asset type + @"""assetType"": ""{0}""," + + + // The asset symbol + @"""symbol"": ""{1}""" + + + "}}," + + + // BUY, SELL, BUY_TO_COVER, SELL_SHORT, BUY_TO_OPEN, + // BUY_TO_CLOSE, SELL_TO_OPEN, SELL_TO_CLOSE, EXCHANGE + @"""instruction"": ""{2}""," + + + // Order quantity, + @"""quantity"": {3}" + + + "}}"; + + //********************************************************************* + // Member: comboOrderTpl + // + /// + /// The template for a combination OPTION order + /// + //********************************************************************* + private static string comboOrderTpl = + "{{" + + + // ORDER STRATEGY + @"""orderStrategyType"": ""{3}""," + + + // MARKET or LIMIT + @"""orderType"": ""{0}""," + + + // The ORDER LEG collection (only one for plugin) + @"""orderLegCollection"": [" + + + // The combo order leg Json + "{4}" + + + "]," + + + // Complex strategy type + //@"""complexOrderStrategyType"": ""CUSTOM""," + + + // DAY, GOOD_TILL_CANCEL, FILL_OR_KILL + @"""duration"": ""{2}""," + + + // NORMAL, AM, PM, SEAMLESS + @"""session"": ""{1}""" + + + "}}"; + + //********************************************************************* + // Member: fundOrderTpl + // + /// + /// The template for a stock order (BUY or SELL) with no STOP + /// + //********************************************************************* + private static string fundOrderTpl = + "{{" + + // MARKET or LIMIT + @"""orderType"": ""{0}""," + + + // NORMAL, AM, PM, SEAMLESS + @"""session"": ""{1}""," + + + // DAY, GOOD_TILL_CANCEL, FILL_OR_KILL + @"""duration"": ""{2}""," + + + // Complex strategy type + @"""complexOrderStrategyType"": ""NONE""," + + + // Price + @"""price"": {5}," + + + // ORDER STRATEGY + @"""orderStrategyType"": ""{3}""," + + + // The ORDER LEG collection (only one for plugin) + @"""orderLegCollection"": [" + + "{{" + + // BUY, SELL, BUY_TO_COVER, SELL_SHORT, BUY_TO_OPEN, + // BUY_TO_CLOSE, SELL_TO_OPEN, SELL_TO_CLOSE, EXCHANGE + @"""instruction"": ""{4}""," + + + // Order type: EQUITY, OPTION, INDEX, MUTUAL_FUND, + // CASH_EQUIVALENT, FIXED_INCOME, CURRENCY + @"""orderLegType"": ""{7}""," + + + // Order quantity, + @"""quantity"": 150," + + + // Order quantity type, + @"""quantityType"": ""DOLLARS""," + + + // The Instrument + @"""instrument"": {{" + + + // The asset symbol + @"""symbol"": ""{6}""," + + + // Asset type + @"""assetType"": ""{7}""," + + + // Fund type ('NOT_APPLICABLE' or 'OPEN_END_NON_TAXABLE' or 'OPEN_END_TAXABLE' or 'NO_LOAD_NON_TAXABLE' or 'NO_LOAD_TAXABLE') + @"""type"": ""NOT_APPLICABLE""" + + "}}" + + "}}" + + "]" + + "}}"; + + //********************************************************************* + // Member: LimitPropsTpl + // + /// + /// The template for the additional JSON properties that need to be + /// added in the case of a LIMIT order. + /// + //********************************************************************* + private static string + LimitPropsTpl = + + // Need to include complexOrderStrategyType + @"""complexOrderStrategyType"": ""NONE""," + + + // Limit price + @"""price"": ""{0}"","; + + //********************************************************************* + // Member: trailingStopTpl + // + /// + /// Template for a TRAILING STOP order at LIMIT or MARKET + /// + //********************************************************************* + private static string trailingStopTpl = + "{{" + + @"""orderType"": ""{0}""," + + @"""session"": ""NORMAL""," + + @"""duration"": ""DAY""," + + @"""orderStrategyType"": ""TRIGGER""," + + @"""orderLegCollection"": [" + + "{{" + + @"""instruction"": ""{1}""," + + @"""quantity"": {2}," + + @"""instrument"": {{" + + @"""symbol"": ""{3}""," + + @"""assetType"": ""{4}""" + + "}}" + + "}}" + + "]," + + @"""childOrderStrategies"": [" + + "{{" + + @"""orderType"": ""TRAILING_STOP""," + + @"""stopPriceOffset"": {5}," + + @"""stopPriceLinkType"": ""VALUE""," + + @"""stopPriceLinkBasis"": ""BID""," + + @"""stopType"": ""STANDARD""," + + @"""session"": ""NORMAL""," + + @"""duration"": ""DAY""," + + @"""orderStrategyType"": ""SINGLE""," + + @"""orderLegCollection"": [" + + "{{" + + @"""instruction"": ""{6}""," + + @"""quantity"": {2}," + + @"""instrument"": {{" + + @"""symbol"": ""{3}""," + + @"""assetType"": ""{4}""" + + "}}" + + "}}" + + "]" + + "}}" + + "]" + + "}}"; + + private bool toClose = false; + #endregion CLASS MEMBERS + + #region CLASS PROPERTIES + //********************************************************************* + // Property: asset + // + /// + /// The underlying TD asset for this order + /// + //********************************************************************* + public TDAsset asset { get; set; } + + //********************************************************************* + // Property: Symbol + // + /// + /// The asset symbol + /// + //********************************************************************* + public string Symbol { get; set; } + + //********************************************************************* + // Property: OrderType + // + /// + /// The type of order (MARKET, LIMIT, STOP). STOP currently not + /// implemented by the plug-in. + /// + //********************************************************************* + public string OrderType { get; set; } + + //********************************************************************* + // Property: Instruction + // + /// + /// The order instruction (BUY or SELL) + /// + //********************************************************************* + public string Instruction { get; set; } + + //********************************************************************* + // Property: Price + // + /// + /// The price for 1 lot of the asset + /// + //********************************************************************* + public double Price { get; set; } + + //********************************************************************* + // Property: Amount + // + /// + /// The asset symbol (i.e. AAPL or MSFT) + /// + //********************************************************************* + public int Amount { get; set; } + + //********************************************************************* + // Property: Limit + // + /// + /// The limit price for this order + /// + //********************************************************************* + public double Limit { get; set; } + + //********************************************************************* + // Property: StopDist + // + /// + /// The stop price offset (not currently implemented) + /// + //********************************************************************* + public double StopDist { get; set; } + + //********************************************************************* + // Property: Session + // + /// + /// Session type (NORMAL, AM, PM) + /// + //********************************************************************* + public string Session { get; set; } + + #endregion CLASS PROPERTIES + + #region CLASS CONSTRUCTOR + //********************************************************************* + // Constructor: OrderSubmission + // + /// + /// Create a class object and set default values for its properties + /// + //********************************************************************* + public OrderSubmission + ( + TDAsset asset, + int Amount, + double Limit, + double StopDist, + bool close = false + ) + { + // Save the paramaters passed to us + this.asset = asset; + this.Amount = Amount; + this.StopDist = StopDist; + this.Limit = Limit; + toClose = close; + + } + #endregion CLASS CONSTRUCTOR + + #region PUBLIC METHODS FOR COMBO ORDERS + //********************************************************************* + // Method: SaveInComboList + // + /// + /// Save the current order in the combo order list. + /// + /// + /// + /// True if successful, false if not. + /// + //********************************************************************* + public bool + SaveInComboList + () + + { + try + { + // Does the combo order list need to be initialized + if (comboOrders == null) comboOrders = new List(); + + // Add this order to the combo order list + comboOrders.Add(this); + + // Return with success + return true; + } + catch(Exception e) + { + // Log error + LogHelper.Log(LogLevel.Error, $" {Resx.GetString("ERROR")}: {Resx.GetString("SAVING_COMBO_OPTION_ORDER")}. " + e.Message); + + // Return with failure + return false; + } + } + + //********************************************************************* + // Method: GetCombinedOrderJson + // + /// + /// Get the JSON payload for a combined OPTION order + /// + /// + /// + /// A Json string representing the combo options order. + /// + //********************************************************************* + public string + GetCombinedOrderJson + () + { + // Method members + string comboLegJson = string.Empty; + string json = string.Empty; + string marketType = null; + double limit = -1; + bool failure = false; + + // Create the order leg collection by interating through the save + // orders + foreach (OrderSubmission order in comboOrders) + { + // Test for limit? No LIMIT orders processed as combo leg + // orders by TD Ameritrade + if (order.Limit > 0) + { + // Log the error + json = $"{Resx.GetString("ERROR").ToUpper()}: {Resx.GetString("NO_LIMIT_IN_COMBE")}"; + + // Combo order has failde + failure = true; + + // Exit loop + break; + } + + // Test for assets other than OPITONS + if (order.asset.AssetType != "OPT") + { + // Log the error + json = $"{Resx.GetString("ERROR").ToUpper()}: {order.asset.AssetType} {Resx.GetString("NOT_IN_COMBO")}"; + + // Combo order has failde + failure = true; + + // Exit loop + break; + } + + // NO: Get the TD Ameritrade asset type + string AssetType = Helper.ZorroToTDAssetType(order.asset.AssetType); + + // Get the Instruction + string instruction = order.Amount > 0 ? "BUY" : "SELL"; + if (AssetType == "OPTION" && !toClose) instruction += "_TO_OPEN"; + if (AssetType == "OPTION" && toClose) instruction += "_TO_CLOSE"; + + // Add the order leg to the combo leg Json + comboLegJson += string.Format(orderLegTpl, + + // Asset type (0) + AssetType, + + // Option symbol (1) + AssetType == "OPTION" ? order.GetOptionSymbol() : order.asset.TickerSymbol, + + // Instruction: Buy or sell to open (2) + instruction, + + // Amount of order (3) + Math.Abs(order.Amount) + ) + ","; + } + + // Has order failed validation? + if (!failure) + { + // NO: Remove the last comma from the combo Json + comboLegJson = comboLegJson.Trim(','); + + // Clear out the combo order list + comboOrders.Clear(); + + // Create the main combo order + json = string.Format( + // Template + comboOrderTpl, + + // Order Type (LIMIT or MARKET) (0) + "MARKET", + + // Session (1) + "NORMAL", + + // Duration (2) + "DAY", + + // Order strategy type (3) + "SINGLE", + + // Combo leg Json (4) + comboLegJson + ); + + + // If this is as LIMIT combo OPTION order, add the LIMIT price + if (Limit > 0) + json = json.Replace("\"orderStrategy", $"\"price\": {Limit}," + "\"orderStrategy"); + } + + // Return the combo Json for order + return json; + } + #endregion PUBLIC METHODS FOR COMBO OPTIONS + + #region PUBLIC METHODS + //********************************************************************* + // Method: ConvertToJson + // + /// + /// Convert the class object to a JSON string based on the type of + /// order. + /// + //********************************************************************* + public string + ConvertToJson + () + { + // Method members + string json = Broker.jsonNull; + + // Switch based on the order type + switch(asset.AssetType) + { + case "ETF": + case "EQUITY": + case "STK": + json = GetStockOrderJson(); + break; + + case "OPTION": + case "OPT": + json = GetOptionsOrderJson(); + break; + + case "MUTUAL_FUND": + json = GetFundOrderJson(); + break; + + default: + break; + } + + // Return the JSON string + return json; + } + + //********************************************************************* + // Method: GetStockOrderJson + // + /// + /// Get the JSON string for a stock order at MARKET or LIMIT. + /// + /// + /// + /// JSON order payload for STK or ETF asset. + /// + //********************************************************************* + public string + GetStockOrderJson + () + { + //***************************************************************** + // NOTE: The TD Ameritrade REST API appears to allow STOP MARKET or + // STOP LIMIT orders. But as of 05/15/20 no STOP MARKET/STOP LIMIT + // orders were accepted. TD Ameritrade has been notified and may + // resolve this matter. But as of this release of the plug-in STOP + // MARKET and STOP LIMIT orders will not be placed. Zorro notes on + // the BrokerBuy2 command state that the STOP DISTANCE included as a + // parameter for this command, "is not the real stop loss, which is + // handled by the trade engine. Placing the stop is not mandatory." + // Consequently, STOP MARKET and STOP LIMIT orders will not be placed + // by this version of the plug-in. + // + // Uncomment the following six lines, if STOP MARKET OR STOP LIMIT + // orders can be placed with the API at some future time. + //***************************************************************** + // Is this a STOP order? + //if (StopDist != 0) + //{ + // YES: Process the STOP order and return the json string + //return GetStopOrderJson(); + //} + + // Get the TD Ameritrade asset type + string AssetType = Helper.ZorroToTDAssetType(asset.AssetType); + + // Get the basic JSON string + string json = string.Format(stockOrderTpl, + + // Order Type (0) + Limit > 0 ? "LIMIT" : "MARKET", + + // Session (1) (TD Ameritrade API only supports NORMAL now) + "NORMAL", + + // Duration of order (2) (TD Ameritrade only suports DAY now) + "DAY", + + // Order strategy (3) (always SINGLE, TRIGGER orders + // not supported by the plug-in) + "SINGLE", + + // Instruction (4) (BUY or SELL) + Amount > 0 ? "BUY" : "SELL", + + // Order amount (5). ABS to cover long and short trades + Math.Abs(Amount), + + // Asset ticker symbol symbol (6) + asset.TickerSymbol, + + // The asset type (7). Convert from Zorro to TD Ameritrade. + Helper.ZorroToTDAssetType(asset.AssetType) + ); + + // If this is a LIMIT order, add LIMIT infomation to the JSON string + if (Limit > 0) + json = json.Replace("\"orderStrategy", string.Format(LimitPropsTpl, Limit) + "\"orderStrategy"); + + // Return the JSON string + return json; + } + + //********************************************************************* + // Method: GetOptionsOrderJson + // + /// + /// Get the JSON string for a Options order at MARKET or LIMIT. + /// + //********************************************************************* + public string + GetOptionsOrderJson + () + { + // Get the TD Ameritrade asset type + string AssetType = Helper.ZorroToTDAssetType(asset.AssetType); + + // Get the basic JSON string + string json = string.Format(optionsOrderTpl, + + // Order Type (0) + Limit > 0 ? "LIMIT" : "MARKET", + + // Session (1) (TD Ameritrade API only supports NORMAL now) + "NORMAL", + + // Duration of order (2) (TD Ameritrade only suports DAY now) + "DAY", + + // Order strategy (3) (always SINGLE, TRIGGER orders + // not supported by the plug-in) + "SINGLE", + + // Instruction (4) (BUY or SELL) + Amount > 0 ? "BUY_TO_OPEN" : "SELL_TO_OPEN", + + // Order amount (5). ABS to cover long and short trades + Math.Abs(Amount), + + // Asset ticker symbol symbol (6) + GetOptionSymbol(), + + // The asset type (7). Convert from Zorro to TD Ameritrade. + Helper.ZorroToTDAssetType(asset.AssetType) + ); + + // If this is a LIMIT order, add LIMIT infomation to the JSON string + if (Limit > 0) + json = json.Replace("\"orderStrategy", string.Format(LimitPropsTpl, Limit) + "\"orderStrategy"); + + // Return the JSON string + return json; + } + + //********************************************************************* + // Method: GetStopOrderJson + // + /// + /// Return an order with a TRAILING STOP at either a LIMIT or MARKET + /// price. + /// + /// + /// + /// String with json for the order. + /// + //********************************************************************* + private string + GetStopOrderJson + () + { + // Json for the orrder: + string json = string.Format( + // Trail STOP LIMIT emplate + trailingStopTpl, + + // Order type (LIMIT or MARKET) (0) + (Limit > 0) ? "LIMIT" : "MARKET", + + // Primary instruction (1) + (Amount > 0) ? "BUY" : "SELL", + + // Quantity (2) + Math.Abs(Amount), + + // Symbol (3) + asset.TickerSymbol, + + // Asset type (4) + Helper.ZorroToTDAssetType(asset.AssetType), + + // Stop price distance (5) + Math.Abs(StopDist), + + // Counter instruction (6) + (Amount > 0) ? "SELL" : "BUY" + ); + + // If this is a LIMIT order, add LIMIT price to the JSON string + if (Limit > 0) + json = json.ReplaceFirst("\"duration", $"\"price\": {Limit}," + "\"duration"); + + // Return the json string + return json; + } + + //********************************************************************* + // Method: GetFundOrderJson + // + /// + /// Get the JSON string for a mutual fund order. + /// + //********************************************************************* + public string + GetFundOrderJson + () + { + // Get the basic JSON string + string json = string.Format(fundOrderTpl, + + // Order Type (0) + OrderType, + + // Session (1) + Session, + + // Duration of order (2) + "DAY", + + // Order strategy (3) (always SINGLE, TRIGGER orders + // not supported by the plug-in) + "SINGLE", + + // Instruction (4) + Instruction, + + // Order amount (5). ABS to cover long and short trades + Amount * Price, + + // Asset symbol (6) + Symbol, + + // The asset type (7) + asset.AssetType + ); + + // If this is a LIMIT order, add LIMIT infomation to the JSON string + if (OrderType == "LIMIT") + json = json.Replace("\"orderStrategy", string.Format(LimitPropsTpl, Limit) + "\"orderStrategy"); + + // Return the JSON string + return json; + } + + //********************************************************************* + // Method: GetOptionSymbol + // + /// + /// Get an option symbol. + /// + /// + /// + /// The option symbol as a string. + /// + /// + /// + /// The option symbol is a string in the form of SSS_MMDDYYXNN, where: + /// SSS = Asset ticker symbol + /// MM = Month of expiration + /// DD = Day of expiration + /// YY = Year of expiration + /// X = "C" for CALL, "P" for PUT + /// NN = The strike price + /// + /// NOTE: The original symbol will all of this information, and it + /// should have been encoded in the asset. + /// + //********************************************************************* + public string + GetOptionSymbol + () + { + // Method member + string optSymbol = string.Empty; + + // Place the ticker symbol on the string + optSymbol = asset.TickerSymbol + "_"; + + // Format the date as a six-character string + string strDate = asset.option.ExpirationDate.ToString("MMddyy"); + + // Create the symbol Stock Ticker + Exp. Date + PUT/CALL + Strike Price + optSymbol += strDate + asset.option.PutCallType + asset.option.StrikePrice.ToString("N2"); + + // Return the option symbol + return optSymbol.Trim('0').Trim('.'); + } + #endregion PUBLIC METHODS + } +} diff --git a/TDAmeritradeZorro/Classes/SessionHours.cs b/TDAmeritradeZorro/Classes/SessionHours.cs new file mode 100644 index 0000000..6f68d7e --- /dev/null +++ b/TDAmeritradeZorro/Classes/SessionHours.cs @@ -0,0 +1,78 @@ +//***************************************************************************** +// File: SessionHours.cs +// +// Author: Clyde W. Ford +// +// Date: April 29, 2020 +// +// Description: Classs for storing start and end market hours. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Collections.Generic; +using System.Runtime.Serialization; +using TDAmeritradeZorro.Classes.TDA; + +namespace TDAmeritradeZorro.Classes +{ + //************************************************************************* + // Class: SessionHours + // + /// + /// A class that has information about the three principal trading hours + /// + /// + /// + /// Hours in UTC (Zulu) time. + /// + //************************************************************************* + [DataContract] + public class SessionHours + { + //********************************************************************* + // Property: PreMarket + // + /// + /// Start and end hours for pre-market trading. + /// + //********************************************************************* + [DataMember(Name = "preMarket")] + public List PreMarket { get; set; } + + //********************************************************************* + // Property: RegularMarket + // + /// + /// Start and end hours for regular market trading. + /// + //********************************************************************* + [DataMember(Name = "regularMarket")] + public List RegularMarket { get; set; } + + //********************************************************************* + // Property: PostMarket + // + /// + /// Start and end hours for post-market trading. + /// + //********************************************************************* + [DataMember(Name = "postMarket")] + public List PostMarket { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/Settings.cs b/TDAmeritradeZorro/Classes/Settings.cs new file mode 100644 index 0000000..fda4e93 --- /dev/null +++ b/TDAmeritradeZorro/Classes/Settings.cs @@ -0,0 +1,161 @@ +//***************************************************************************** +// File: Settings.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The setting class object +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System; +using System.IO; +using System.Runtime.Serialization; +using TDAmeritradeZorro.Utilities; + +namespace TDAmeritradeZorro.Classes +{ + //************************************************************************* + // Class: Settings + // + /// + /// A class that encapsulates settings information entered by the user into + /// a text file as JSON data, which is then converted to this C# object. + /// + //************************************************************************* + [DataContract] + public class Settings + { + //********************************************************************* + // Property: Currency + // + /// + /// The account currency (USD, EUR, JPY) etc. + /// + //********************************************************************* + [DataMember(Name = "currency")] + public string Currency { get; set; } + + //********************************************************************* + // Property: TdaAccountNum + // + /// + /// The TD Ameritrade user account number. + /// + //********************************************************************* + [DataMember(Name = "tdaAccountNum")] + public string TdaAccountNum { get; set; } + + //********************************************************************* + // Property: clientId + // + /// + /// The TD Ameritrade client id (consumer key) for the developer app + /// related to the user. + /// + //********************************************************************* + [DataMember(Name = "clientId")] + public string ClientId { get; set; } + + //********************************************************************* + // Property: LangResx + // + /// + /// The default language resource file specified as ll-CC, where ll = + /// the language (i.e. en, es, de) and CC = the country (i.e. US, GB, + /// MX). + /// + //********************************************************************* + [DataMember(Name = "langResx")] + public string LangResx { get; set; } + + //********************************************************************* + // Property: testAssets + // + /// + /// Ticker symbols for the asset used for testing the plug-in. + /// + //********************************************************************* + [DataMember(Name = "testAssets")] + public string testAssets { get; set; } + + //********************************************************************* + // Method: Read + // + /// + /// Read user settings from the settings file + /// + /// + /// + /// A Settings object. + /// + //********************************************************************* + public static Settings + Read + () + { + // Method members + string settingsFile = Broker.WORKING_DIR + Broker.SETTINGS_FILE; + string json = string.Empty; + + try + { + // Does the settings file exist? + if (File.Exists(settingsFile)) + { + // YES: Iterate through each line of the settings file + foreach (string line in File + .ReadAllLines(Broker.WORKING_DIR + Broker.SETTINGS_FILE)) + { + // Do not add line if it contains a double-slash or is blank + if (line.Contains("//") || + string.IsNullOrEmpty(line.Trim())) continue; + + // Add line to json string + json += line; + } + + // Does json string have data? + if (!string.IsNullOrEmpty(json)) + + // YES: Convert to Settings class object and return + return Broker.DeserializeJson(json); + else + + // NO: Log this error + LogHelper.Log(LogLevel.Error, $"{Resx.GetString("ERROR")}: tda.json {Resx.GetString("INCORRECT_FORMAT")}."); + } + else + { + // Settings file does not exist, log that error + LogHelper.Log(LogLevel.Critical, $"{Resx.GetString("ERROR")}: tda.json {Resx.GetString("FILE_NOT_FOUND")}."); + } + } + catch(Exception e) + { + // A file I/O error occurred, log it + LogHelper.Log(LogLevel.Critical, $"{Resx.GetString("ERROR")}: {Resx.GetString("FILE_IO_ERROR")} {Resx.GetString("READING").ToLower()} tda.json."); + } + + // If we get here return a NULL class object + return null; + } + } +} \ No newline at end of file diff --git a/TDAmeritradeZorro/Classes/StrikePriceInfo.cs b/TDAmeritradeZorro/Classes/StrikePriceInfo.cs new file mode 100644 index 0000000..b3d1578 --- /dev/null +++ b/TDAmeritradeZorro/Classes/StrikePriceInfo.cs @@ -0,0 +1,56 @@ +//***************************************************************************** +// File: StrikePriceInfo.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: A contract expiration date map for option chains. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes +{ + [DataContract] + public class StrikePriceInfo + { + [DataMember (Name = "putCall")] + public string PutCall { get; set; } + + [DataMember(Name = "symbol")] + public string Symbol { get; set; } + + [DataMember(Name = "description")] + public string Description{ get; set; } + + [DataMember(Name = "bid")] + public double Bid { get; set; } + + [DataMember(Name = "ask")] + public double Ask { get; set; } + + [DataMember(Name = "timeValue")] + public double TimeValue { get; set; } + + [DataMember(Name = "totalVolume")] + public double TotalVolume { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/StringExtensions.cs b/TDAmeritradeZorro/Classes/StringExtensions.cs new file mode 100644 index 0000000..e09e069 --- /dev/null +++ b/TDAmeritradeZorro/Classes/StringExtensions.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TDAmeritradeZorro.Classes +{ + public static class StringExtensions + { + public static string ReplaceFirst(this string text, string search, string replace) + { + int pos = text.IndexOf(search); + if (pos < 0) + { + return text; + } + return text.Substring(0, pos) + replace + text.Substring(pos + search.Length); + } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/AccountBalance.cs b/TDAmeritradeZorro/Classes/TDA/AccountBalance.cs new file mode 100644 index 0000000..043cc91 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/AccountBalance.cs @@ -0,0 +1,80 @@ +//***************************************************************************** +// File: AccountBalance.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The AccountBalance class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // Class: AccountBalance + // + /// + /// A class that encapsulates the information required by Zorro in response + /// to the BrokerAccount command, which returns the current account status. + /// + //************************************************************************* + public class AccountBalance + { + //********************************************************************* + // Member: AccountId + // + /// + /// The TD Ameritrade account id. + /// + //********************************************************************* + public string AccountId { get; set; } + + //********************************************************************* + // Member: Balance + // + /// + /// The current balance on the account. + /// + //********************************************************************* + public double Balance { get; set; } + + //********************************************************************* + // Member: TradeValue + // + /// + /// The current value of all open trades, computed as: + /// + /// TRADE VALUE = ACCOUNT EQUITY - ACCOUNT BALANCE + /// + /// + //********************************************************************* + public double TradeValue { get; set; } + + //********************************************************************* + // Member: MarginValue + // + /// + /// The total margin bound by all open trades. + /// + //********************************************************************* + public double MarginValue { get; set; } + + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/Assets/EquityETF.cs b/TDAmeritradeZorro/Classes/TDA/Assets/EquityETF.cs new file mode 100644 index 0000000..74356c7 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/Assets/EquityETF.cs @@ -0,0 +1,112 @@ +//***************************************************************************** +// File: EquityETF.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The EquityETF class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; +using TDAmeritradeZorro.Interface; + +namespace TDAmeritradeZorro.Classes.TDA.Assets +{ + //************************************************************************* + // Class: EquityETF + // + /// The EquityETF class object. See the TD Ameritrade API information at + /// https://developer.tdameritrade.com/apis for details on the properties + /// and usage of this class. + //************************************************************************* + [DataContract] + public class EquityETF : OHLCBase, IPrice + { + [DataMember(Name = "bidPrice")] + public double BidPrice { get; set; } + + [DataMember(Name = "bidSize")] + public int BidSize { get; set; } + + [DataMember(Name = "bidId")] + public string BidId { get; set; } + + [DataMember(Name = "askPrice")] + public double AskPrice { get; set; } + + [DataMember(Name = "askSize")] + public int AskSize { get; set; } + + [DataMember(Name = "askId")] + public string AskId { get; set; } + + [DataMember(Name = "lastSize")] + public int LastSize { get; set; } + + [DataMember(Name = "lastId")] + public string LastId { get; set; } + + [DataMember(Name = "bidTick")] + public string BidTick { get; set; } + + [DataMember(Name = "quoteTimeInLong")] + public long QuoteTimeInLong { get; set; } + + [DataMember(Name = "mark")] + public double Mark { get; set; } + + [DataMember(Name = "marginable")] + public bool IsMarginable { get; set; } + + [DataMember(Name = "shortable")] + public bool IsShortable { get; set; } + + [DataMember(Name = "volatility")] + public double Volatility { get; set; } + + [DataMember(Name = "regularMarketLastPrice")] + public double RegularMarketLastPrice { get; set; } + + [DataMember(Name = "regularMarketLastSize")] + public int RegularMarketLastSize { get; set; } + + [DataMember(Name = "regularMarketNetChange")] + public double RegularMarketNetChange { get; set; } + + [DataMember(Name = "regularMarketTradeTimeInLong")] + public long RegularMarketTradeTimeInLong { get; set; } + + [DataMember(Name = "netPercentChangeInDouble")] + public double NetPercentChangeInDouble { get; set; } + + [DataMember(Name = "markChangeInDouble")] + public double MarkChangeInDouble { get; set; } + + [DataMember(Name = "markPercentChangeInDouble")] + public double MarkPercentChangeInDouble { get; set; } + + [DataMember(Name = "regularMarketPercentChangeInDouble")] + public double RegularMarketPercentChangeInDouble { get; set; } + + [DataMember(Name = "delayed")] + public bool IsDelayed { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/Assets/Forex.cs b/TDAmeritradeZorro/Classes/TDA/Assets/Forex.cs new file mode 100644 index 0000000..42bb94e --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/Assets/Forex.cs @@ -0,0 +1,68 @@ +//***************************************************************************** +// File: Forex.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The Forex class object, used in trading currency pairs. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; +using TDAmeritradeZorro.Interface; + +namespace TDAmeritradeZorro.Classes.TDA.Assets +{ + //************************************************************************* + // Class: Forex + // + /// The Forex class object. See the TD Ameritrade API information at + /// https://developer.tdameritrade.com/apis for details on the properties + /// and usage of this class. + //************************************************************************* + [DataContract] + public class Forex : ForexBase, IPrice + { + [DataMember(Name = "changeInDouble")] + public double Change { get; set; } + + [DataMember(Name = "52WkHighInDouble")] + public double High52Wk { get; set; } + + [DataMember(Name = "52WkLowInDouble")] + public double Low52Wk { get; set; } + + [DataMember(Name = "percentChange")] + public double PercentChange { get; set; } + + [DataMember(Name = "product")] + public string Product { get; set; } + + [DataMember(Name = "tradingHours")] + public string TradingHours { get; set; } + + [DataMember(Name = "isTradable")] + public bool IsTradable { get; set; } + + [DataMember(Name = "marketMaker")] + public string marketMaker { get; set; } + + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/Assets/Future.cs b/TDAmeritradeZorro/Classes/TDA/Assets/Future.cs new file mode 100644 index 0000000..81abd0c --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/Assets/Future.cs @@ -0,0 +1,69 @@ +//***************************************************************************** +// File: Future.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The Future class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA.Assets +{ + //************************************************************************* + // Class: Future + // + /// The Future class object. See the TD Ameritrade API information at + /// https://developer.tdameritrade.com/apis for details on the properties + /// and usage of this class. + //************************************************************************* + [DataContract] + public class Future + { + [DataMember(Name = "changeInDouble")] + public double ChangeInDouble { get; set; } + + [DataMember(Name = "lastId")] + public string LastId { get; set; } + + [DataMember(Name = "bidId")] + public string BidId { get; set; } + + [DataMember(Name = "askId")] + public string AskId { get; set; } + + [DataMember(Name = "product")] + public string Product { get; set; } + + [DataMember(Name = "futurePriceFormat")] + public string FuturePriceFormat { get; set; } + + [DataMember(Name = "futureMultiplier")] + public double FutureMultiplier { get; set; } + + [DataMember(Name = "futureSettlementPrice")] + public double FutureSettlementPrice { get; set; } + + [DataMember(Name = "futureActiveSymbol")] + public string FutureActiveSymbol { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/Assets/FutureOption.cs b/TDAmeritradeZorro/Classes/TDA/Assets/FutureOption.cs new file mode 100644 index 0000000..f76c549 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/Assets/FutureOption.cs @@ -0,0 +1,90 @@ +//***************************************************************************** +// File: FutureOption.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The FutureOption class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA.Assets +{ + //************************************************************************* + // Class: FutureOption + // + /// The FutureOption class object. See the TD Ameritrade API information at + /// https://developer.tdameritrade.com/apis for details on the properties + /// and usage of this class. + //************************************************************************* + [DataContract] + public class FutureOption : FutureBase + { + [DataMember(Name = "volatility")] + public double Volatility { get; set; } + + [DataMember(Name = "netChangeInDouble")] + public double NetChange { get; set; } + + [DataMember(Name = "moneyIntrinsicValueInDouble")] + public double MoneyIntrinsicValue { get; set; } + + [DataMember(Name = "multiplierInDouble")] + public double Multiplier { get; set; } + + [DataMember(Name = "strikePriceInDouble")] + public double StrikePrice { get; set; } + + [DataMember(Name = "timeValueInDouble")] + public double TimeValue { get; set; } + + [DataMember(Name = "deltaInDouble")] + public double Delta { get; set; } + + [DataMember(Name = "gammaInDouble")] + public double Gamma { get; set; } + + [DataMember(Name = "thetaInDouble")] + public double Theta { get; set; } + + [DataMember(Name = "vegaInDouble")] + public double Vega { get; set; } + + [DataMember(Name = "rhoInDouble")] + public double Rho { get; set; } + + [DataMember(Name = "expirationType")] + public string expirationType { get; set; } + + [DataMember(Name = "exerciseType")] + public string exerciseType { get; set; } + + [DataMember(Name = "inTheMoney")] + public string inTheMoney { get; set; } + + [DataMember(Name = "contractType")] + public string contractType { get; set; } + + [DataMember(Name = "underlying")] + public string underlying { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/Assets/Index.cs b/TDAmeritradeZorro/Classes/TDA/Assets/Index.cs new file mode 100644 index 0000000..da30a20 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/Assets/Index.cs @@ -0,0 +1,41 @@ +//***************************************************************************** +// File: Index.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The Index class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +namespace TDAmeritradeZorro.Classes.TDA.Assets +{ + //************************************************************************* + // Class: Index + // + /// The Index class object. See the TD Ameritrade API information at + /// https://developer.tdameritrade.com/apis for details on the properties + /// and usage of this class. + //************************************************************************* + + public class Index : OHLCBase + { + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/Assets/MutualFund.cs b/TDAmeritradeZorro/Classes/TDA/Assets/MutualFund.cs new file mode 100644 index 0000000..ce6aae0 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/Assets/MutualFund.cs @@ -0,0 +1,50 @@ +//***************************************************************************** +// File: MutualFund.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The MutualFund class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using TDAmeritradeZorro.Interface; + +namespace TDAmeritradeZorro.Classes.TDA.Assets +{ + //************************************************************************* + // Class: MutualFund + // + /// The MutualFund class object. See the TD Ameritrade API information at + /// https://developer.tdameritrade.com/apis for details on the properties + /// and usage of this class. + //************************************************************************* + public class MutualFund : DivBase, IPrice + { + public double AskPrice { get; set; } + public double BidPrice { get; set; } + public double LastPrice { get; set; } + + public MutualFund() + { + AskPrice = BidPrice = LastPrice = 0.00; + } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/Assets/Option.cs b/TDAmeritradeZorro/Classes/TDA/Assets/Option.cs new file mode 100644 index 0000000..14f88bd --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/Assets/Option.cs @@ -0,0 +1,115 @@ +//***************************************************************************** +// File: Option.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The Option class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; +using TDAmeritradeZorro.Interface; + +namespace TDAmeritradeZorro.Classes.TDA.Assets +{ + //************************************************************************* + // Class: Option + // + /// The Option class object. See the TD Ameritrade API information at + /// https://developer.tdameritrade.com/apis for details on the properties + /// and usage of this class. + //************************************************************************* + [DataContract] + public class Option : OHLCBase, IPrice + { + [DataMember(Name = "bidPrice")] + public double BidPrice { get; set; } + + [DataMember(Name = "bidSize")] + public double BidSize { get; set; } + + [DataMember(Name = "askPrice")] + public double AskPrice { get; set; } + + [DataMember(Name = "askSize")] + public double AskSize { get; set; } + + [DataMember(Name = "quoteTimeInLong")] + public long QuoteTimeInLong { get; set; } + + [DataMember(Name = "mark")] + public double Mark { get; set; } + + [DataMember(Name = "openInterest")] + public double OpenInterest { get; set; } + + [DataMember(Name = "volatility")] + public double Volatility { get; set; } + + [DataMember(Name = "moneyIntrinsicValue")] + public double MoneyIntrinsicValue { get; set; } + + [DataMember(Name = "multiplier")] + public double Multiplier { get; set; } + + [DataMember(Name = "strikePrice")] + public double StrikePrice { get; set; } + + [DataMember(Name = "timeValue")] + public double TimeValue { get; set; } + + [DataMember(Name = "delta")] + public double Delta { get; set; } + + [DataMember(Name = "gamma")] + public double Gamma { get; set; } + + [DataMember(Name = "theta")] + public double Theta { get; set; } + + [DataMember(Name = "vega")] + public double Vega { get; set; } + + [DataMember(Name = "rho")] + public double Rho { get; set; } + + [DataMember(Name = "theoreticalOptionValue")] + public double TheoreticalOptionValue { get; set; } + + [DataMember(Name = "underlyingPrice")] + public double UnderlyingPrice { get; set; } + + [DataMember(Name = "deliverables")] + public string Deliverables { get; set; } + + [DataMember(Name = "contractType")] + public string contractType { get; set; } + + [DataMember(Name = "underlying")] + public string Underlying { get; set; } + + [DataMember(Name = "uvExpirationType")] + public string UVExpirationType { get; set; } + + [DataMember(Name = "settlementType")] + public string SettlementType { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/Base/AssetBase.cs b/TDAmeritradeZorro/Classes/TDA/Base/AssetBase.cs new file mode 100644 index 0000000..17ec25c --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/Base/AssetBase.cs @@ -0,0 +1,89 @@ +//***************************************************************************** +// File: AssetBase.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The Asset base class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA.Assets +{ + //************************************************************************* + // Class: AssetBase + // + /// + /// The Asset base class object. See the TD Ameritrade API information at + /// https://developer.tdameritrade.com/apis for details on the properties + /// and usage of this class. + /// + //************************************************************************* + [DataContract] + public class AssetBase + { + [DataMember(Name = "assetType")] + public string AssetType { get; set; } + + [DataMember(Name = "assetMainType")] + public string AssetMainType { get; set; } + + [DataMember(Name = "cusip")] + public string Cusip { get; set; } + + [DataMember(Name = "symbol")] + public string Symbol { get; set; } + + [DataMember(Name = "description")] + public string Description { get; set; } + + [DataMember(Name = "exchange")] + public string Exchange { get; set; } + + [DataMember(Name = "exchangeName")] + public string ExchangeName { get; set; } + [DataMember(Name = "52WkHigh")] + public double WkHigh52 { get; set; } + + [DataMember(Name = "52WkLow")] + public double WkLow52 { get; set; } + + [DataMember(Name = "closePrice")] + public double ClosePrice { get; set; } + + [DataMember(Name = "tradeTimeInLong")] + public long TradeTimeInLong { get; set; } + + [DataMember(Name = "netChange")] + public double NetChange { get; set; } + + [DataMember(Name = "totalVolume")] + public double TotalVolume { get; set; } + + [DataMember(Name = "digits")] + public int Digits { get; set; } + + [DataMember(Name = "securityStatus")] + public string SecurityStatus { get; set; } + + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/Base/DivBase.cs b/TDAmeritradeZorro/Classes/TDA/Base/DivBase.cs new file mode 100644 index 0000000..fd87ba7 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/Base/DivBase.cs @@ -0,0 +1,63 @@ +//***************************************************************************** +// File: DivBase.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The Div (Dividend) base class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA.Assets +{ + //************************************************************************* + // Class: DivBase + // + /// + /// The Div base class object. See the TD Ameritrade API information at + /// https://developer.tdameritrade.com/apis for details on the properties + /// and usage of this class. + /// + /// + /// + /// This class is related to dividends for a given asset. + /// + //************************************************************************* + [DataContract] + public class DivBase : AssetBase + { + [DataMember(Name = "nAV")] + public double NAV { get; set; } + + [DataMember(Name = "peRatio")] + public double PERatio { get; set; } + + [DataMember(Name = "divAmount")] + public double DivAmount { get; set; } + + [DataMember(Name = "divYield")] + public double DivYield { get; set; } + + [DataMember(Name = "divDate")] + public string DivDate { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/Base/DoubleBase.cs b/TDAmeritradeZorro/Classes/TDA/Base/DoubleBase.cs new file mode 100644 index 0000000..289ccab --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/Base/DoubleBase.cs @@ -0,0 +1,79 @@ +//***************************************************************************** +// File: DoubleBale.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The Double base class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA.Assets +{ + [DataContract] + //************************************************************************* + // Class: DoubleBase + // + /// + /// The Double base class object. See the TD Ameritrade API information at + /// https://developer.tdameritrade.com/apis for details on the properties + /// and usage of this class. + /// + /// + /// + /// NOTE: Some properties are given by the TD Ameritrade API as floats and + /// others of a similar name are given as doubles. + /// + //************************************************************************* + public class DoubleBase + { + [DataMember(Name = "bidPriceInDouble")] + public double BidPriceInDouble { get; set; } + + [DataMember(Name = "askPriceInDouble")] + public double AskPriceInDouble { get; set; } + + [DataMember(Name = "lastPriceInDouble")] + public double LastPriceInDouble { get; set; } + + [DataMember(Name = "highPriceInDouble")] + public double HighPriceInDouble { get; set; } + + [DataMember(Name = "lowPriceInDouble")] + public double LowPriceInDouble { get; set; } + + [DataMember(Name = "closePriceInDouble")] + public double ClosePriceInDouble { get; set; } + + [DataMember(Name = "openPriceInDouble")] + public double OpenPriceInDouble { get; set; } + + [DataMember(Name = "mark")] + public double Mark { get; set; } + + [DataMember(Name = "tick")] + public double Tick { get; set; } + + [DataMember(Name = "tickAmount")] + public double TickAmount { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/Base/ForexBase.cs b/TDAmeritradeZorro/Classes/TDA/Base/ForexBase.cs new file mode 100644 index 0000000..39b11cc --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/Base/ForexBase.cs @@ -0,0 +1,74 @@ +//***************************************************************************** +// File: ForexBase.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The OHLC base class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA.Assets +{ + //************************************************************************* + // Class: ForexBase + // + /// + /// The Forex base class object. See the TD Ameritrade API information at + /// https://developer.tdameritrade.com/apis for details on the properties + /// and usage of this class. + /// + //************************************************************************* + [DataContract] + public class ForexBase : AssetBase + { + [DataMember(Name = "bidPriceInDouble")] + public double BidPrice { get; set; } + + [DataMember(Name = "askPriceInDouble")] + public double AskPrice { get; set; } + + [DataMember(Name = "lastPriceInDouble")] + public double LastPrice { get; set; } + + [DataMember(Name = "highPriceInDouble")] + public double HighPrice { get; set; } + + [DataMember(Name = "lowPriceInDouble")] + public double LowPrice { get; set; } + + [DataMember(Name = "closePriceInDouble")] + public double ForexClosePrice { get; set; } + + [DataMember(Name = "openPriceInDouble")] + public double OpenPrice { get; set; } + + [DataMember(Name = "mark")] + public double Mark { get; set; } + + [DataMember(Name = "tick")] + public double Tick { get; set; } + + [DataMember(Name = "tickAmount")] + public double TickAmount { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/Base/FutureBase.cs b/TDAmeritradeZorro/Classes/TDA/Base/FutureBase.cs new file mode 100644 index 0000000..c9bb2df --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/Base/FutureBase.cs @@ -0,0 +1,63 @@ +//***************************************************************************** +// File: FutureBase.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The Future base class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System; +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA.Assets +{ + //************************************************************************* + // Class: OHLCBase + // + /// + /// The Future base class object. See the TD Ameritrade API information at + /// https://developer.tdameritrade.com/apis for details on the properties + /// and usage of this class. + /// + //************************************************************************* + [DataContract] + public class FutureBase : DoubleBase + { + [DataMember(Name = "openInterest")] + public double OpenInterest { get; set; } + + [DataMember(Name = "futureExpirationDate")] + public DateTime FutureExpirationDate { get; set; } + + [DataMember(Name = "futurePercentChange")] + public double FuturePercentChange { get; set; } + + [DataMember(Name = "futureTradingHours")] + public double FutureTradingHours { get; set; } + + [DataMember(Name = "futureIsTradable")] + public bool IsFutureTradable { get; set; } + + [DataMember(Name = "futureIsActive")] + public bool IsFutureActive { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/Base/OHLCBase.cs b/TDAmeritradeZorro/Classes/TDA/Base/OHLCBase.cs new file mode 100644 index 0000000..09c0f82 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/Base/OHLCBase.cs @@ -0,0 +1,58 @@ +//***************************************************************************** +// File: OHLCBase.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The OHLC base class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA.Assets +{ + //************************************************************************* + // Class: OHLCBase + // + /// + /// The OHLC base class object. See OHLC class object + /// and the TD Ameritrade API information at + /// https://developer.tdameritrade.com/apis for details on the properties + /// and usage of this class. + /// + //************************************************************************* + [DataContract] + public class OHLCBase : DivBase + { + [DataMember(Name = "openPrice")] + public double OpenPrice { get; set; } + + [DataMember(Name = "highPrice")] + public double HighPrice { get; set; } + + [DataMember(Name = "lowPrice")] + public double LowPrice { get; set; } + + [DataMember(Name = "lastPrice")] + public double LastPrice { get; set; } + + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/CurrencyInterestRates.cs b/TDAmeritradeZorro/Classes/TDA/CurrencyInterestRates.cs new file mode 100644 index 0000000..2cea170 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/CurrencyInterestRates.cs @@ -0,0 +1,416 @@ +//***************************************************************************** +// File: CurrencyInterestRates.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The CurrencyInterestRates class. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using TDAmeritradeZorro.Utilities; + +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // + // NOT SUPPORTED: FOREX trading (currency pairs) is not currently supported + // in the TD Ameritrade API. This class is included for FUTURE USE ONLY. + // + //************************************************************************* + + //************************************************************************* + // Class: CurrencyIntrestRates + // + /// + /// A class for computing and maintain current currency interest rates used + /// for computing, among other things, Forex rollover rates charged for + /// currency trades held overnight. + /// + /// + /// + /// NOTE: These interest rates are being obtained from the TradingEconomics + /// website, esentially through "page scraping." Since the format of that + /// website can change without warning, the current process of obtaining + /// these rates may be invalid without warning as well. + /// + //************************************************************************* + public class CurrencyInterestRates + { + #region CLASS MEMBERS (PRIVATE) + //********************************************************************* + // Member: BaseUrl + // + /// + /// The Trading Economics website page with a table of current currency + /// interest rates. + /// + //********************************************************************* + private static readonly string BaseUrl = "https://tradingeconomics.com/"; + + //********************************************************************* + // Member: SingleUrlTpl + // + /// + /// The Trading Economics website page for a single country that is + /// not found in the main page table. + /// + //********************************************************************* + private static readonly string SingleUrlTpl = + "https://tradingeconomics.com/{0}/interest-rate"; + + //********************************************************************* + // Member: TimeRead + // + /// + /// The date and time the current table on the Trading Economics site + /// was last read. + /// + //********************************************************************* + private static DateTime TimeRead = DateTime.MinValue; + + //********************************************************************* + // Member: ReadTimeExpiration + // + /// + /// The maximum number of hours before re-reading currency interest + /// rates from the Trading Economics website. + /// + //********************************************************************* + private static readonly double ReadTimeExpiration = 24; + + //********************************************************************* + // Member: InterestRateDictionary + // + /// + /// A dictionary of currency symobls/interests rate for major FOREX + /// pairs. + /// + //********************************************************************* + public static Dictionary InterestRateDict; + + //********************************************************************* + // Member: CurrencyDict + // + /// + /// A dictionary of currency symobls/country names for major FOREX + /// pairs. + /// + //********************************************************************* + public static Dictionary CurrencyDict = new Dictionary() + { + {"JPY", "Japan" }, + {"CAD", "Canada" }, + {"CNH", "China" }, + {"CNY", "China" }, + {"AUD", "Australia" }, + {"NZD", "New Zealand" }, + {"GBP", "United Kingdom" }, + {"EUR", "Euro Area" }, + {"CHF", "Switzerland" }, + {"HKD", "Hong Kong" }, + {"USD", "United States" }, + {"MXN", "Mexico" }, + }; + #endregion CLASS MEMBERS (PRIVATE) + + #region CLASS METHODS (PRIVATE) + //********************************************************************* + // Method: GetCurrencyInterestRates + // + /// + /// Get the current central bank interest rates for a given FOREX pair. + /// + /// + /// + /// The currency pair for mhich interest rates are sought. + /// + /// + /// + /// A double array with values representing the interest rate for the + /// currency pair, [0] element of the array contains the base currency + /// interest rade, [1] element of the array contains the quote currency + /// interest rade. + /// + //********************************************************************* + private static double[] + GetCurrencyInterestRates + ( + string forexPair + ) + { + // If not a valid currency pair, return 0.0 + if (forexPair.Length != 7 || !forexPair.Contains("/")) + return new double[] {0.0, 0.0 }; + + // Break apart the currencies in the pair + string[] currencies = forexPair.Split('/'); + + // Get the IRB + double IRB = GetInterestRate(currencies[0]); + + // Get the IRQ + double IRQ = GetInterestRate(currencies[1]); + + // Return a double array + return new double[] { IRB, IRQ }; + } + #endregion CLASS METHODS (PRIVATE) + + #region CLASS METHODS (PUBLIC) + //********************************************************************* + // Method: InitCurrencyInterestRates + // + /// + /// Initialize the currency interest rates. + /// + /// + /// + /// Currency interest rates are determined by the central banks of the + /// country from which the currency derives. There are a few sources of + /// these rates online but they each have their challenges. This method + /// determines currency interest rates from either: + /// + /// (1) global-rates.com, one page for all CB interest rates except + /// the Hong Kong Dollar (HKD); and, + /// + /// (2) tradingeconomics.com, for the Hong Kong Dollar (HKD) + /// + //********************************************************************* + public static bool + InitCurrencyInterestRates + () + { + // Method members + Match m; + double rate = Double.MinValue; + string result; + + // Regex to extract the interest rate for a given country + string reCountryTpl = @"/{0}/interest-rate(.*?)>(.*?)<"; + + try + { + // Initialize the currency interest rate dictionary + InterestRateDict = new Dictionary(); + + // Read the global rates document + result = Helper.GetWebPage(BaseUrl); + + // Save the read time + TimeRead = DateTime.UtcNow; + + // Iterate through the country keys + foreach (string key in CurrencyDict.Keys) + { + // Create a Regex for this country + Regex reCountry = new Regex(string.Format(reCountryTpl, CurrencyDict[key]).ToLower().Replace(" ", "-")); + + // Locate the country and the interest rate + m = reCountry.Match(result); + + // Was the country found? + if (m.Groups.Count > 1) + { + // YES: Get the interest rate + rate = Double.Parse(m.Groups[2].Value.Replace("%", "")); + } + else + { + // NO: Look-up the counttry with the single page URL + rate = GetSingleCountryInterestRate(key); + } + + // If interst rate error returned, skip this country + if (rate < -900.00) continue; + + // Add currency interest rate and country to the dictionary + if (InterestRateDict.ContainsKey(key)) + { + // UPDATE: + InterestRateDict[key] = rate; + } + else + { + // ADD: + InterestRateDict.Add(key, rate); + } + } + + // Return a success code + return true; + } + catch(Exception e) + { + // Log the error + LogHelper.Log(LogLevel.Error, $" {Resx.GetString("ERROR")}: {Resx.GetString("CANNOT_GET_INTEREST_RATES")}. " + e.Message); + + // Initialize the interest rate dictionary + InterestRateDict = new Dictionary(); + } + + // Return a failure code + return false; + } + + public static double + GetSingleCountryInterestRate + ( + string key + ) + { + // Method members + Regex reCountry2 = new Regex(@"(.*\d)"); + string result; + Match m; + double rate = -999.99; + + // Get the single page for this country + result = Helper.GetWebPage(string.Format( + // Template + SingleUrlTpl, + + // Country name after looking it up and replace spaces with + // hyphens all to lower case + CurrencyDict[key].ToLower().Replace(" ", "-")) + ); + + // Was the page found? + if (!string.IsNullOrEmpty(result)) + { + // YES: Look for a match + m = reCountry2.Match(result); + + // Match found? + if (m.Success) + { + // YES: Get the interest rate + rate = Convert.ToDouble(m.Groups[1].Value); + } + } + + // Return the interst rate found + return rate; + } + + //********************************************************************* + // Method: GetInterestRate + // + /// + /// Get the current central bank interest rate for a given currency. + /// + /// + /// + /// The currency for which a CB interest rate is being sougth. + /// + /// + /// + /// A double value representing the interest rate for the currency. + /// + //********************************************************************* + public static double + GetInterestRate + ( + string currency + ) + { + try + { + // Is the read time of the interest rates document older than the + // set amount? + if (DateTime.UtcNow.Subtract(TimeRead).TotalHours + > ReadTimeExpiration || !InterestRateDict.ContainsKey(currency)) + { + // Re-initialize the currency interest rate table + InitCurrencyInterestRates(); + } + + // Return the rate converted to a decimal + return InterestRateDict[currency]; + } + catch (Exception e) + { + LogHelper.Log(LogLevel.Error, $" {Resx.GetString("ERROR")}: For {currency} " + e.Message); + } + + return 0.0; + } + + //********************************************************************* + // Method: ComputeRollover + // + /// + /// Computer thet FOREX rollover (interest debited or credited) to an + /// account. + /// + /// + /// + /// The currency pair for mhich rollover interest is being computed. + /// + /// + /// + /// A value representing the rollover interest for this currency pair. + /// + /// + /// + /// Rollover is computed based on the following formula: + /// + /// RO = (IRB - IRQ)/(365 * E), where: + /// + /// RO = The rollover rate + /// IRB = The interest rate for the base currency (first in pair) + /// IRQ = The interest rate for the quote currency (second in pair) + /// E = The exchange rate for the currency pair + /// + //********************************************************************* + public static double + ComputeRollover + ( + string forexPair, + double E + ) + { + // Method members + double RO; + + // Get the IRB and IRQ + double[] rates = GetCurrencyInterestRates(forexPair); + + // Compute the rollover rate + RO = (rates[0] - rates[1]) / (365.0 * E); + + /* + Console.WriteLine("RO = (IRB - IRQ)/(365 * E)"); + Console.WriteLine($"For {forexPair}..."); + Console.WriteLine($" IRB = {rates[0]}"); + Console.WriteLine($" IRQ = {rates[1]}"); + Console.WriteLine($" E = {E}"); + Console.WriteLine($"Rollover = {RO}\r\n"); + */ + + // Return the rollover rate + return RO; + } + #endregion CLASS METHODS (PUBLIC) + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/CurrentBalances.cs b/TDAmeritradeZorro/Classes/TDA/CurrentBalances.cs new file mode 100644 index 0000000..fe4da09 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/CurrentBalances.cs @@ -0,0 +1,125 @@ +//***************************************************************************** +// File: CurrentBalances.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The CurrentBalances class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // Class: CurrentBalances + // + /// + /// The TD Ameritrade CurrentBalances class. See the TD Ameritrade API + /// information at https://developer.tdameritrade.com/apis for details on + /// the properties of this class. + /// + //************************************************************************* + [DataContract] + public class CurrentBalances + { + [DataMember(Name = "accruedInterest")] + public double AccruedInterest { get; set; } + + [DataMember(Name = "cashBalance")] + public double CashBalance { get; set; } + + [DataMember(Name = "cashReceipts")] + public double CashReceipts { get; set; } + + [DataMember(Name = "longOptionMarketValue")] + public double LongOptionMarketValue { get; set; } + + [DataMember(Name = "liquidationValue")] + public double LiquidationValue { get; set; } + + [DataMember(Name = "longMarketValue")] + public double LongMarketValue { get; set; } + + [DataMember(Name = "moneyMarketFund")] + public double MoneyMarketFund { get; set; } + + [DataMember(Name = "savings")] + public double Savings { get; set; } + + [DataMember(Name = "shortMarketValue")] + public double ShortMarketValue { get; set; } + + [DataMember(Name = "pendingDeposits")] + public double PendingDeposits { get; set; } + + [DataMember(Name = "availableFunds")] + public double AvailableFunds { get; set; } + + [DataMember(Name = "availableFundsNonMarginableTrade")] + public double AvailableFundsNonMarginableTrade { get; set; } + + [DataMember(Name = "buyingPower")] + public double BuyingPower { get; set; } + + [DataMember(Name = "buyingPowerNonMarginableTrade")] + public double BuyingPowerNonMarginableTrade { get; set; } + + [DataMember(Name = "dayTradingBuyingPower")] + public double DayTradingBuyingPower { get; set; } + + [DataMember(Name = "equity")] + public double Equity { get; set; } + + [DataMember(Name = "equityPercentage")] + public double EquityPercentage { get; set; } + + [DataMember(Name = "longMarginValue")] + public double LongMarginValue { get; set; } + + [DataMember(Name = "maintenanceCall")] + public double MaintenanceCall { get; set; } + + [DataMember(Name = "maintenanceRequirement")] + public double MaintenanceRequirement { get; set; } + + [DataMember(Name = "marginBalance")] + public double MarginBalance { get; set; } + + [DataMember(Name = "regTCall")] + public double RegTCall { get; set; } + + [DataMember(Name = "shortBalance")] + public double ShortBalance { get; set; } + + [DataMember(Name = "shortMarginValue")] + public double ShortMarginValue { get; set; } + + [DataMember(Name = "shortOptionMarketValue")] + public double ShortOptionMarketValue { get; set; } + + [DataMember(Name = "sma")] + public double SMA { get; set; } + + [DataMember(Name = "bondValue")] + public double BondValue { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/ExecutionLeg.cs b/TDAmeritradeZorro/Classes/TDA/ExecutionLeg.cs new file mode 100644 index 0000000..5e9de29 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/ExecutionLeg.cs @@ -0,0 +1,60 @@ +//***************************************************************************** +// File: SecuritiesAccount.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The ExecutionLeg class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System; +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // Class: ExecutionLeg + // + /// + /// The TD Ameritrade ExecutionLeg class. See the TD Ameritrade API + /// information at https://developer.tdameritrade.com/apis for details on + /// the properties of this class. + /// + //************************************************************************* + [DataContract] + public class ExecutionLeg + { + [DataMember(Name = "legId")] + public int LegId { get; set; } + + [DataMember(Name = "quantity")] + public int Quantity { get; set; } + + [DataMember(Name = "mismarkedQuantity")] + public int MismarkedQuantity{ get; set; } + + [DataMember(Name = "price")] + public double Price{ get; set; } + + [DataMember(Name = "time")] + public DateTime Time { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/InitialBalances.cs b/TDAmeritradeZorro/Classes/TDA/InitialBalances.cs new file mode 100644 index 0000000..31a9193 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/InitialBalances.cs @@ -0,0 +1,141 @@ +//***************************************************************************** +// File: InitialBalances.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The InitialBalances class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // Class: InitialBalances + // + /// + /// The TD Ameritrade InitialBalances class. See the TD Ameritrade API + /// information at https://developer.tdameritrade.com/apis for details on + /// the properties of this class. + /// + //************************************************************************* + [DataContract (Name = "initialBalances")] + public class InitialBalances + { + [DataMember(Name = "accruedInterest")] + public double AccruedInterest { get; set; } + + [DataMember(Name = "availableFundsNonMarginableTrade")] + public double AvailableFundsNonMarginableTrade { get; set; } + + [DataMember(Name = "bondValue")] + public double BondValue { get; set; } + + [DataMember(Name = "buyingPower")] + public double BuyingPower { get; set; } + + [DataMember(Name = "cashBalance")] + public double CashBalance { get; set; } + + [DataMember(Name = "cashAvailableForTrading")] + public double CashAvailableForTrading { get; set; } + + [DataMember(Name = "cashReceipts")] + public double CashReceipts { get; set; } + + [DataMember(Name = "dayTradingBuyingPower")] + public double BayTradingBuyingPower { get; set; } + + [DataMember(Name = "dayTradingBuyingPowerCall")] + public double BayTradingBuyingPowerCall { get; set; } + + [DataMember(Name = "dayTradingEquityCall")] + public double BayTradingEquityCall { get; set; } + + [DataMember(Name = "equity")] + public double Equity { get; set; } + + [DataMember(Name = "equityPercentage")] + public double EquityPercentage { get; set; } + + [DataMember(Name = "liquidationValue")] + public double LiquidationValue { get; set; } + + [DataMember(Name = "longMarginValue")] + public double LongMarginValue { get; set; } + + [DataMember(Name = "longOptionMarketValue")] + public double LongOptionMarketValue { get; set; } + + [DataMember(Name = "longStockValue")] + public double LongStockValue { get; set; } + + [DataMember(Name = "maintenanceCall")] + public double MaintenanceCall { get; set; } + + [DataMember(Name = "maintenanceRequirement")] + public double MaintenanceRequirement { get; set; } + + [DataMember(Name = "margin")] + public double Margin { get; set; } + + [DataMember(Name = "marginEquity")] + public double MarginEquity { get; set; } + + [DataMember(Name = "moneyMarketFund")] + public double MoneyMarketFund { get; set; } + + [DataMember(Name = "mutualFundValue")] + public double MutualFundValue { get; set; } + + [DataMember(Name = "regTCall")] + public double RegTCall { get; set; } + + [DataMember(Name = "shortMarginValue")] + public double ShortMarginValue { get; set; } + + [DataMember(Name = "shortOptionMarketValue")] + public double ShortOptionMarketValue { get; set; } + + [DataMember(Name = "shortStockValue")] + public double ShortStockValue { get; set; } + + [DataMember(Name = "totalCash")] + public double TotalCash { get; set; } + + [DataMember(Name = "isInCall")] + public bool IsInCall { get; set; } + + [DataMember(Name = "pendingDeposits")] + public double PendingDeposits { get; set; } + + [DataMember(Name = "marginBalance")] + public double MarginBalance { get; set; } + + [DataMember(Name = "shortBalance")] + public double ShortBalance { get; set; } + + [DataMember(Name = "accountValue")] + public double AccountValue { get; set; } + + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/Instrument.cs b/TDAmeritradeZorro/Classes/TDA/Instrument.cs new file mode 100644 index 0000000..19717fc --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/Instrument.cs @@ -0,0 +1,59 @@ +//***************************************************************************** +// File: Instrument.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The Instrument class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // Class: Instrument + // + /// + /// The TD Ameritrade Instrument class. See the TD Ameritrade API + /// information at https://developer.tdameritrade.com/apis for details on + /// the properties of this class. + /// + //************************************************************************* + [DataContract(Name = "instrument")] + public class Instrument + { + [DataMember(Name = "assetType")] + public string AssetType { get; set; } + + [DataMember(Name = "cusip")] + public string Cusip { get; set; } + + [DataMember(Name = "symbol")] + public string Symbol { get; set; } + + [DataMember(Name = "description")] + public string Description { get; set; } + + [DataMember(Name = "type")] + public string Type { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/MarketHours.cs b/TDAmeritradeZorro/Classes/TDA/MarketHours.cs new file mode 100644 index 0000000..ba322ea --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/MarketHours.cs @@ -0,0 +1,70 @@ +//***************************************************************************** +// File: MarketHours.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The SecuritiesAccount class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // Class: MarketHours + // + /// + /// The TD Ameritrade MarketHours class. See the TD Ameritrade API + /// information at https://developer.tdameritrade.com/apis for details on + /// the properties of this class. + /// + //************************************************************************* + [DataContract] + public class MarketHours + { + [DataMember(Name = "date")] + public string Date { get; set; } + + [DataMember(Name = "marketType")] + public string MarketType { get; set; } + + [DataMember(Name = "exchange")] + public string Exchange { get; set; } + + [DataMember(Name = "category")] + public string Category { get; set; } + + [DataMember(Name = "product")] + public string Product { get; set; } + + [DataMember(Name = "productName")] + public string ProductName { get; set; } + + [DataMember(Name = "isOpen")] + public bool IsOpen { get; set; } + + [DataMember(Name = "sessionHours")] + public SessionHours HoursOpen { get; set; } + + public double ServerTime { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/OHLC.cs b/TDAmeritradeZorro/Classes/TDA/OHLC.cs new file mode 100644 index 0000000..306888b --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/OHLC.cs @@ -0,0 +1,62 @@ +//***************************************************************************** +// File: OHLC.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The OHLC (open, high, low, close) class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // Class: OHLC + // + /// + /// The TD Ameritrade OHLC (open, high, low, close) class. See the TD + /// Ameritrade API information at https://developer.tdameritrade.com/apis + /// for details on the properties of this class. + /// + //************************************************************************* + [DataContract] + public class OHLC + { + [DataMember(Name = "open")] + public double Open { get; set; } + + [DataMember(Name = "high")] + public double High { get; set; } + + [DataMember(Name = "low")] + public double Low { get; set; } + + [DataMember(Name = "close")] + public double Close { get; set; } + + [DataMember (Name = "volume")] + public int Volume { get; set; } + + [DataMember (Name = "datetime")] + public long Date { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/OptionChain.cs b/TDAmeritradeZorro/Classes/TDA/OptionChain.cs new file mode 100644 index 0000000..e22544b --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/OptionChain.cs @@ -0,0 +1,48 @@ +//***************************************************************************** +// File: OptionChain.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: An underlying asset option chain. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA +{ + [DataContract] + public class OptionChain + { + [DataMember(Name = "symbol")] + public string Symbol { get; set; } + + [DataMember(Name = "status")] + public string Status { get; set; } + + [DataMember(Name = "underlyingPrice")] + public double UnderlyingPrice { get; set; } + + [DataMember(Name = "numberOfContracts")] + public int NumberOfContracts { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/Order.cs b/TDAmeritradeZorro/Classes/TDA/Order.cs new file mode 100644 index 0000000..4004e04 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/Order.cs @@ -0,0 +1,118 @@ +//***************************************************************************** +// File: Order.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The Order class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // Class: Order + // + /// + /// The TD Ameritrade Order class. See the TD Ameritrade API + /// information at https://developer.tdameritrade.com/apis for details on + /// the properties of this class. + /// + //************************************************************************* + [DataContract] + public class Order + { + [DataMember(Name = "session")] + public string Session { get; set; } + + [DataMember(Name = "duration")] + public string Duration { get; set; } + + [DataMember(Name = "orderType")] + public string OrderType { get; set; } + + [DataMember(Name = "complexOrderStrategyType")] + public string ComplexOrderStrategyType { get; set; } + + [DataMember(Name = "quantity")] + public int Quantity { get; set; } + + [DataMember(Name = "price")] + public double Price { get; set; } + + [DataMember(Name = "stopPrice")] + public double StopPrice { get; set; } + + [DataMember(Name = "stopPriceLinkBasis")] + public string StopPriceLinkBasis { get; set; } + + [DataMember(Name = "stopPriceLinkType")] + public string StopPriceLinkType { get; set; } + + [DataMember(Name = "stopPriceOffset")] + public double StopPriceOffset { get; set; } + + [DataMember(Name = "stopType")] + public double StopType { get; set; } + + [DataMember(Name = "filledQuantity")] + public int FilledQuantity { get; set; } + + [DataMember(Name = "remainingQuantity")] + public int RemainingQuantity { get; set; } + + [DataMember(Name = "requestedDestination")] + public string RequestedDestination { get; set; } + + [DataMember(Name = "destinationLinkName")] + public string DestinationLinkName { get; set; } + + [DataMember(Name = "orderLegCollection")] + public List OrderLegCollection { get; set; } + + [DataMember(Name = "orderActivityCollection")] + public List OrderAttivityCollection { get; set; } + + [DataMember(Name = "orderStrategyType")] + public string OrderStrategyType { get; set; } + + [DataMember(Name = "orderId")] + public long OrderId { get; set; } + + [DataMember(Name = "cancelable")] + public bool IsCancelable { get; set; } + + [DataMember(Name = "editable")] + public bool IsEditable { get; set; } + + [DataMember(Name = "status")] + public string Status { get; set; } + + [DataMember(Name = "enteredTime")] + public DateTime EnteredTime { get; set; } + + [DataMember(Name = "accountId")] + public long AccountId { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/OrderActivity.cs b/TDAmeritradeZorro/Classes/TDA/OrderActivity.cs new file mode 100644 index 0000000..f8d9e1e --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/OrderActivity.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace TDAmeritradeZorro.Classes.TDA +{ + [DataContract] + public class OrderActivity + { + [DataMember(Name = "activityType")] + public string ActivityType { get; set; } + + [DataMember(Name = "executionType")] + public string ExecutionType { get; set; } + + [DataMember(Name = "quantity")] + public int Quantity { get; set; } + + [DataMember(Name = "orderRemainingQuantity")] + public int OrderRemainingQuantity { get; set; } + + [DataMember(Name = "executionLegs")] + public List ExecutionLegs { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/OrderLeg.cs b/TDAmeritradeZorro/Classes/TDA/OrderLeg.cs new file mode 100644 index 0000000..df88fe2 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/OrderLeg.cs @@ -0,0 +1,26 @@ +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA +{ + [DataContract] + public class OrderLeg + { + [DataMember(Name = "orderLegType")] + public string OrderItemType { get; set; } + + [DataMember(Name = "legId")] + public int LegId { get; set; } + + [DataMember(Name = "instrument")] + public Instrument Instrument { get; set; } + + [DataMember(Name = "instruction")] + public string Instruction { get; set; } + + [DataMember(Name = "positionEffect")] + public string PositionEffect { get; set; } + + [DataMember(Name = "quantity")] + public double Quantity { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/Positions.cs b/TDAmeritradeZorro/Classes/TDA/Positions.cs new file mode 100644 index 0000000..b83e04b --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/Positions.cs @@ -0,0 +1,71 @@ +//***************************************************************************** +// File: Positions.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The Position class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // Class: Position + // + /// + /// The TD Ameritrade Position class. See the TD Ameritrade API + /// information at https://developer.tdameritrade.com/apis for details on + /// the properties of this class. + /// + //************************************************************************* + [DataContract] + public class Position + { + [DataMember(Name = "shortQuantity")] + public double ShortQuantity { get; set; } + + [DataMember(Name = "averagePrice")] + public double AveragePrice { get; set; } + + [DataMember(Name = "currentDayProfitLoss")] + public double CurrentDayProfitLoss { get; set; } + + [DataMember(Name = "currentDayProfitLossPercentage")] + public double CurrentDayProfitLossPercentage { get; set; } + + [DataMember(Name = "longQuantity")] + public double LongQuantity { get; set; } + + [DataMember(Name = "settledLongQuantity")] + public double SettledLongQuantity { get; set; } + + [DataMember(Name = "settledShortQuantity")] + public double SettledShortQuantity { get; set; } + + [DataMember(Name = "instrument")] + public Instrument Instrument { get; set; } + + [DataMember(Name = "marketValue")] + public double MarketValue { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/PriceHistory.cs b/TDAmeritradeZorro/Classes/TDA/PriceHistory.cs new file mode 100644 index 0000000..db27514 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/PriceHistory.cs @@ -0,0 +1,54 @@ +//***************************************************************************** +// File: PriceHistory.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The PriceHistory class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // Class: PriceHistory + // + /// + /// The TD Ameritrade PriceHistory class. See the TD Ameritrade API + /// information at https://developer.tdameritrade.com/apis for details on + /// the properties for this class. + /// + //************************************************************************* + [DataContract] + public class PriceHistory + { + [DataMember(Name = "symbol")] + public string Symbol { get; set; } + + [DataMember(Name = "candles")] + public List Candles { get; set; } + + [DataMember(Name = "Empty")] + public bool IsEmpty { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/ProjectedBalances.cs b/TDAmeritradeZorro/Classes/TDA/ProjectedBalances.cs new file mode 100644 index 0000000..9294211 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/ProjectedBalances.cs @@ -0,0 +1,71 @@ +//***************************************************************************** +// File: ProjectBalances.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The ProjectedBalances class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // Class: ProjectedBalances + // + /// + /// The TD Ameritrade ProjectedBalances class. See the TD Ameritrade API + /// information at https://developer.tdameritrade.com/apis for details on + /// the properties of this class. + /// + //************************************************************************* + [DataContract(Name = "projectedBalances")] + public class ProjectedBalances + { + [DataMember(Name = "availableFunds")] + public double AvailableFunds { get; set; } + + [DataMember(Name = "availableFundsNonMarginableTrade")] + public double AvailableFundsNonMarginableTrade { get; set; } + + [DataMember(Name = "buyingPower")] + public double BuyingPower { get; set; } + + [DataMember(Name = "dayTradingBuyingPower")] + public double DayTradingBuyingPower { get; set; } + + [DataMember(Name = "dayTradingBuyingPowerCall")] + public double DayTradingBuyingPowerCall { get; set; } + + [DataMember(Name = "maintenanceCall")] + public double MaintenanceCall { get; set; } + + [DataMember(Name = "regTCall")] + public double RegTCall { get; set; } + + [DataMember(Name = "isInCall")] + public bool IsInCall { get; set; } + + [DataMember(Name = "stockBuyingPower")] + public double StockBuyingPower { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/SecuritiesAccount.cs b/TDAmeritradeZorro/Classes/TDA/SecuritiesAccount.cs new file mode 100644 index 0000000..945b480 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/SecuritiesAccount.cs @@ -0,0 +1,77 @@ +//***************************************************************************** +// File: SecuritiesAccount.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: The SecuritiesAccount class object. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // Class: SecuritiesAccount + // + /// + /// The TD Ameritrade SecuritiesAccount class. See the TD Ameritrade API + /// information at https://developer.tdameritrade.com/apis for details on + /// the properties of this class. + /// + //************************************************************************* + [DataContract(Name = "securitiesAccount")] + public class SecuritiesAccount + { + [DataMember(Name = "type")] + public string Type { get; set; } + + [DataMember(Name = "accountId")] + public string AccountId { get; set; } + + [DataMember(Name = "roundTrips")] + public int RoundTrips { get; set; } + + [DataMember(Name = "isDayTrader")] + public bool IsDayTrader { get; set; } + + [DataMember(Name = "isClosingOnlyRestricted")] + public bool IsClosingOnlyRestricted { get; set; } + [DataMember(Name = "positions")] + public List Positions { get; set; } + + [DataMember(Name = "orderStrategies")] + public List Orders { get; set; } + + [DataMember(Name = "initialBalances")] + public InitialBalances InitBalances { get; set; } + + [DataMember(Name = "currentBalances")] + public CurrentBalances CurrBalances { get; set; } + + [DataMember(Name = "projectedBalances")] + public ProjectedBalances ProjBalances { get; set; } + public SecuritiesAccount() + { + } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/StartEndHours.cs b/TDAmeritradeZorro/Classes/TDA/StartEndHours.cs new file mode 100644 index 0000000..bdef233 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/StartEndHours.cs @@ -0,0 +1,65 @@ +//***************************************************************************** +// File: StartEndHours.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: Start and end hours class +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System; +using System.Runtime.Serialization; + +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // Class: StartEndHours + // + /// + /// A class that holds stsarting and ending hours as date time objects. This + /// class is used by the SessionHours object to hold start and end times for + /// the three principal markets. + /// + //************************************************************************* + [DataContract] + public class StartEndHours + { + //********************************************************************* + // Property: Start + // + /// + /// The start hour. + /// + //********************************************************************* + [DataMember(Name = "start")] + public DateTime Start { get; set; } + + //********************************************************************* + // Property: End + // + /// + /// The ending hour. + /// + //********************************************************************* + [DataMember(Name = "end")] + public DateTime End { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/TDAsset.cs b/TDAmeritradeZorro/Classes/TDA/TDAsset.cs new file mode 100644 index 0000000..a74c405 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/TDAsset.cs @@ -0,0 +1,1010 @@ +//***************************************************************************** +// File: TDAsset.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: A class to hold information about a particular asset. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** + +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using TDAmeritradeZorro.Utilities; + +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // Class: TDAsset + // + /// + /// Class to encapsulates all of the properties required by Zorro's Broker + /// Asset method. + /// + //************************************************************************* + public class TDAsset + { + #region CLASS MEMBERS + //********************************************************************* + // Member: CallCodes + // + /// + /// Delivery month codes for OPTION CALLS + /// + //********************************************************************* + private string CallCodes = "ABCDEFGHIJKL"; + + //********************************************************************* + // Member: PutCodes + // + /// + /// Delivery month codes for OPTION PUTS + /// + //********************************************************************* + private string PutCodes = "MNOPQRSTUVWX"; + + //********************************************************************* + // Member: FutureCodes + // + /// + /// Delivery month codes for FUTURES + /// + //********************************************************************* + private string FutureCodes = "FGHJKMNQUVXZ"; + + //********************************************************************* + // Member: AllowableAssetTypes + // + /// + /// List of allowable asset types. + /// + //********************************************************************* + private List AllowableAssetTypes = new List + { + "STK", "OPT", "FUT", "FUTX", "IND", "FOP", "WAR", "CASH", "CFD", + "STKCFD", "FUND", "EFP", "BAG", "BOND", "CMDTY" + }; + + //********************************************************************* + // Member: AllowableExchanges + // + /// + /// List of allowable exchanges. + /// + //********************************************************************* + private List AllowableExchanges = new List + { + "SMART", "AMEX", "ARCA", "BELFOX", "BOX", "BRUT", "BTRADE", "CBOE", + "CBOT", "CFE", "CME", "DTB", "E-CBOT", "ECBOT", "EUREX US", "FOREX", + "FTA", "GLOBEX", "HKFE", "IBIS", "ICE", "IDEM", "IDEALPRO", "ISE", + "ISLAND", "LIFFE", "LSE", "MATIF", "ME", "MEFFRV", "MONEP", "NYBOT", + "NYMEX", "NYSE", "ONE", "OSE.JPN PHLX", "PSE", "SNFE", "SOFFEX", + "SUPERMONTAGE", "SWX", "TSE", "TSE.JPN", "TSX", "VIRTX", "XETRA" + }; + #endregion CLASS MEMBERS + + //********************************************************************* + // Property: Symbol + // + /// + /// The original symbol string, i.e., + /// AAPL-OPT-20191218-1350.0-C-NYSE/ARCA/GLOBEX-EUR + /// + //********************************************************************* + public string Symbol { get; set; } + + //********************************************************************* + // Property: TickerSymbol + // + /// + /// The ticker symbol only + /// + //********************************************************************* + public string TickerSymbol { get; set; } + + //********************************************************************* + // Property: PrimaryCurrency + // + /// + /// For FOREX currency pair. + /// + //********************************************************************* + public string PrimaryCurrency { get; set; } + + //********************************************************************* + // Property: AssetType + // + /// + /// Asset type from the original symbol (STK, OPT, FUND, CMDTY, etc) + /// + //********************************************************************* + public string AssetType { get; set; } + + //********************************************************************* + // Property: TDAssetType + // + /// + /// TD Ameritrade asset type, i.e. "EQUITY", "OPTION". "ETF", + /// "MUTUAL FUND", etc. + /// + //********************************************************************* + public string TDAssetType { get; set; } + + //********************************************************************* + // Property: Exchange + // + /// + /// The exchanged (NYSE, NASDAQ, AMEX, etc.) the asset trades on. + /// + //********************************************************************* + public string Exchange { get; set; } + + //********************************************************************* + // Property: Price + // + /// + /// (OPTIONAL): Current ask price of the asset, or NULL for + /// subscribing the asset. An asset must be subscribed before any + /// information about it can be retrieved. + /// + //********************************************************************* + public double Price { get; set; } + + //********************************************************************* + // Property: Spread + // + /// + /// (OPTIONAL): Current difference of ask and bid price of the asset. + /// + //********************************************************************* + public double Spread { get; set; } + + //********************************************************************* + // Property: Volume + // + /// + /// (OPTIONAL): Current trade volume of the asset, or 0 when the volume + /// is unavailable, as for currencies, indexes, or CFDs. + /// + //********************************************************************* + public double Volume { get; set; } + + //********************************************************************* + // Property: Pip + // + /// + /// (OPTIONAL): Size of 1 PIP, e.g. 0.0001 for EUR/USD, 0.0 for an JPY + /// FOREX pair. + /// + //********************************************************************* + public double Pip { get; set; } + + //********************************************************************* + // Property: PipCost + // + /// + /// (OPTIONAL): Calculated cost of 1 Pip. + /// + //********************************************************************* + public double PipCost { get; set; } + + //********************************************************************* + // Property: LotAmount + // + /// + /// (OPTIONAL): Minimum order size, i.e. number of contracts for 1 lot + /// of the asset. For currencies on TD Ameritrade it's 10000 for 1 lot. + /// + //********************************************************************* + public double LotAmount { get; set; } + + //********************************************************************* + // Property: MarginCost + // + /// + /// (OPTIONAL): Required margin for buying 1 lot of the asset in units + /// of the account currency. Determines the leverage. Calculate, if + /// needed. + /// + //********************************************************************* + public double MarginCost { get; set; } + + //********************************************************************* + // Property: option + // + /// + /// If this is an OPTION asset, then information about the OPTION + /// + //********************************************************************* + public OptionAsset option { get; set; } + + //********************************************************************* + // Property: futures + // + /// + /// If this is a FUTURES asset, then information about the FUTURES. + /// + //********************************************************************* + public FutuersAsset futures { get; set; } + + //********************************************************************* + // Property: forex + // + /// + /// If this is an FOREX asset, then information about the currency + /// pair. + /// + //********************************************************************* + public ForexAsset forex { get; set; } + + //********************************************************************* + // Property: Valid + // + /// + /// Whether the symbol string is valid + /// + /// + /// + /// True if valid, false if not. + /// + //********************************************************************* + public bool Valid { get; set; } + + #region CLASS CONSTRUCTORS + //********************************************************************* + // Constructor: TDAsset + // + /// + /// The class constructor + /// + /// + /// + /// The asset string, this constructor is called with. + /// + //********************************************************************* + public TDAsset + ( + string assetStr + ) + { + // Save the original asset string + Symbol = assetStr; + + // Parse the asset string and fill out the class properties + Parse(); + } + + // Parameterless constructor + public TDAsset() { } + #endregion CLASS CONSTRUCTOR + + //********************************************************************* + // Method: Parse + // + /// + /// Parse the symbol, which is the original asset string. + /// + //********************************************************************* + private void + Parse + () + { + // Method members + string[] currency; + string[] parts; + string type; + string part; + + try + { + // Split the Symbol by hyphens + parts = Symbol.Split('-'); + + // Are there parts available? + if (parts.Length > 0) + { + //********************************************************* + // + // SECTION 1: TICKER SYMBOL + // + //********************************************************* + if (!parts[0].Contains("/")) + { + // NO: Get the stock ticker symbol + TickerSymbol = parts[0].Trim(); + } + else + { + // YES: Split the currency + currency = parts[0].Split('/'); + + // Get the primary and counter currencies + PrimaryCurrency = currency[0]; + + // Does the secondary currency exist? + if (currency.Length > 1) + { + // YES: Initialize the Forex asset of this TDAsset + if (forex == null) forex = new ForexAsset(); + + // Save the counter currency + forex.CounterCurrency = currency[1]; + } + } + + // If there is only one part, then return now + if (parts.Length == 1) + { + // Assumed to be a stock + AssetType = "STK"; + + // Assumed to be NYSE + Exchange = "NYSE"; + + // Assumed to be default currency + PrimaryCurrency = Broker.settings.Currency; + + // Symbol is valid + Valid = true; + + // Return + return; + } + + //********************************************************* + // + // SECTION 2: ASSET TYPE + // + //********************************************************* + type = parts[1].Trim(); + + // Validate against the allowable asset types. Does the + // allowable asset types list contain this type? + if (AllowableAssetTypes.Contains(type)) + { + // YES: Sove it + AssetType = type; + } + else + { + // NO: Not an allowable asset type. Not valid string. + Valid = false; + } + + // If only two parts, then return what we have so far + if (parts.Length == 2) + { + // Only two parts, an asset and an asset type, exchange + // and counter currency may be omitted. Currency is + // omitted, so use the default currency. + PrimaryCurrency = Broker.settings.Currency; + + // Exchange is ommitted, so use NYSE + Exchange = "NYSE"; + + // Set asset symbol as valid + Valid = true; + + // Return with what we have so sfar + return; + } + + // AT THIS POINT, SHOULD HAVE WHETHER THE SYMBOL IS FOR: + // (1) an OPTION (OPT); (2) a FUTURES (FUT); or, (3) a + // FUTURE OPTION (FOP) + // + // Switch based on asset type + switch (AssetType) + { + case "FOP": + case "OPT": + GetOptions(parts); + break; + + case "FUT": + case "FUTX": + GetFutures(parts); + break; + + case "STK": + default: + // Get the remaining parts of the symbol code + GetEquities(parts); + break; + } + } + } + catch (Exception e) + { + // Log the error + LogHelper.Log(LogLevel.Error, $"{Resx.GetString("ERROR")}: {e.Message}"); + } + } + + //********************************************************************* + // Method: GetEquities + // + /// + /// Get the asset information for an equity (STOCK or ETF) + /// + /// + /// + /// An array containing the split apart original symbol string. + /// + //********************************************************************* + private void + GetEquities + ( + string[] parts + ) + { + // Method members + string part; + List exchanges = new List(); + List excList = new List(); + + //********************************************************* + // + // SECTION 3: Exchange + // + //********************************************************* + GetExchange(parts[2].Trim()); + + // If only three parts, then return the asset symbol + if (parts.Length == 3) + { + // Currency is missing, use the default currency + PrimaryCurrency = Broker.settings.Currency; + + // Assert asset is valid and return + Valid = true; + + // Return with what we have + return; + } + + //********************************************************* + // + // SECTION 3: Currency + // + //********************************************************* + + // Get the currency + part = parts[3]; + + // Is this currency is no within the list of currencies supported, + // use the USD. + if (!CurrencyInterestRates.CurrencyDict.ContainsKey(part)) + part = "USD"; + + // Save the currency + PrimaryCurrency = part; + + // Asset is valid + Valid = true; + } + + //********************************************************************* + // Method: GetEquities + // + /// + /// Get the asset information for an OPTION (OPT or FOP) + /// + /// + /// + /// An array containing the split apart original symbol string. + /// + //********************************************************************* + private void + GetOptions + ( + string[] parts + ) + { + // Method members + string part; + double price; + DateTime date; + + // Initialize the options section + if (option == null) option = new OptionAsset(); + + //********************************************************* + // + // SECTION 3: EXPIRATION DATE (YYYYMMDD) + // + //********************************************************* + part = parts[2].Trim(); + + // Call expiration date function + GetExpirationDate(part); + + // If expiration date resulted in invalid asset symbol, + // just return + if (!Valid) return; + + // If only three parts, then return what we have sofar + if (parts.Length == 3) + { + // Option is not valid + Valid = false; + + // Return the asset symbol + return; + } + + //********************************************************* + // + // SECTION 4: Strike price + // + //********************************************************* + part = parts[3].Trim(); + + // Parse a double value. Is it valid? + if (Double.TryParse(part, out price)) + { + // YES: Save the strike price + option.StrikePrice = price; + } + + // If only four parts, then return what we have sofar + if (parts.Length == 4) + { + // Option is not valid + Valid = false; + + // Return the asset symbol + return; + } + + //********************************************************* + // + // SECTION 5: PUT or CALL + // + //********************************************************* + part = parts[4].Trim(); + + // PUT or CALL? + if (part == "C" || part == "P") + { + // YES: Save the PUT or CALL + option.PutCallType = part; + + // Asset is valid + Valid = true; + } + else + { + // Option is not valid + Valid = false; + + // Return + return; + } + + // if only five parts return asset + if (parts.Length == 5) + { + // Exchange not given, assume it is NYSE + Exchange = "NYSE"; + + // Asset is valid + Valid = true; + + // Return + return; + } + + //********************************************************* + // + // SECTION 6: Exchange + // + //********************************************************* + GetExchange(parts[5].Trim()); + + // if only six parts return asset + if (parts.Length == 6) + { + // Currency not given, assume it is default + PrimaryCurrency = Broker.settings.Currency; + + // Asset is valid + Valid = true; + + // Return + return; + } + + //********************************************************* + // + // SECTION 7: Currency + // + //********************************************************* + part = parts[6].Trim(); + + // Get the currency from the asset symbol string OR from + // the default currency in the settings file + if (CurrencyInterestRates.CurrencyDict.ContainsKey(part)) + PrimaryCurrency = part; + else + PrimaryCurrency = Broker.settings.Currency; + + // Have a valid asset now + Valid = true; + } + + //********************************************************************* + // Method: GetFutures + // + /// + /// Get information for a FUT OR FUTX asset. + /// + /// + /// + /// An array containing the split apart original symbol string. + /// + //********************************************************************* + private void + GetFutures + ( + string[] parts + ) + { + // Method members + DateTime date; + string part; + + // Initialize the FUTURE section + if (futures == null) futures = new FutuersAsset(); + + //********************************************************* + // + // SECTION 3: EXPIRATION DATE (YYYYMMDD) + // + //********************************************************* + part = parts[2].Trim(); + + // Call expiration date function + GetExpirationDate(part); + + // If expiration date resulted in invalid asset symbol, + // just return + if (!Valid) return; + + // If only three parts, then return what we have sofar + if (parts.Length == 3) + { + // Option is not valid + Valid = false; + + // Return the asset symbol + return; + } + + //********************************************************* + // + // SECTION 4: TRADING CLASS + // + //********************************************************* + part = parts[3].Trim(); + + // Get the trading class + if (part != Symbol) futures.TradingClass = part; + + // If only four parts, then return what we have sofar + if (parts.Length == 4) + { + // Option is valid + Valid = true; + + // Exchange in NYSE + Exchange = "NYSE"; + + // Currency is defauld + PrimaryCurrency = Broker.settings.Currency; + + // Return + return; + } + + //********************************************************* + // + // SECTION 5: Exchange + // + //********************************************************* + GetExchange(parts[4].Trim()); + + // if only five parts return asset + if (parts.Length == 5) + { + // Currency not given, assume it is default + PrimaryCurrency = Broker.settings.Currency; + + // Asset is valid + Valid = true; + + // Return the asset symbol + return; + } + + //********************************************************* + // + // SECTION 6: Currency + // + //********************************************************* + part = parts[5].Trim(); + + // Get the currency from the asset symbol string OR from + // the default currency in the settings file + if (CurrencyInterestRates.CurrencyDict.ContainsKey(part)) + PrimaryCurrency = part; + else + PrimaryCurrency = Broker.settings.Currency; + + // Have a valid asset now + Valid = true; + } + + //********************************************************************* + // Method: GetExchange + // + /// + /// Get the exchange(s) for a given asset + /// + /// + /// + /// The exchange string, which may have one or more exchanges separated + /// by forward slashes. + /// + //********************************************************************* + private void + GetExchange + ( + string exc + ) + { + // Method members + List exchanges = new List(); + List excList = new List(); + + // If the exchange contains "/" separate out the exchanges + if (exc.Contains("/")) exchanges.AddRange(exc.Split('/')); + else exchanges.Add(exc); + + // Iterate through the exchanges + foreach (string exchange in exchanges) + { + // Is this an allowable exchange? + if (AllowableExchanges.Contains(exchange)) + { + // YES: Save it. + excList.Add(exchange); + } + else + { + // NO: Flag as invalid asset symbol and return + Valid = false; + return; + } + } + + // Recombine the exchanges, separated by a comma this time + // and save them + Exchange = string.Join(",", excList); + } + + //********************************************************************* + // Method: GetExpirationDate + // + /// + /// Get the expiration date + /// + /// + /// + /// The expiration date as a string. + /// + /// + /// + /// The expiration date can be in one of two formats: + /// (1) YYYYMMDD; OR + /// (2) XN, where X is the month code and N is the last digit of the + /// year. + /// + //********************************************************************* + private void + GetExpirationDate + ( + string strDate + ) + { + // Method members + string monthCode; + int monthIndex; + int yearCode; + DateTime date; + + // Set the asset symbol to not valid + Valid = false; + + try + { + // Are there only numbers in the date string? + if (!Regex.IsMatch(strDate, @"^[A-Za-z]")) + { + // YES: Parse of date time value good? + if (DateTime.TryParse($"{strDate.Substring(0, 4)}-{strDate.Substring(4, 2)}-{strDate.Substring(6, 2)}", out date)) + { + // YES: Save the date + if (AssetType == "OPT") option.ExpirationDate = date; + else futures.ExpirationDate = date; + + // Set validity of option based on exp. date? + Valid = date > DateTime.UtcNow; + + // Nothing more to process + return; + } + } + else + { + // NO: An alphanumeric expiration date string should be in form + // of SSSSXN, where SSS is the symbol of the asset, X is the + // month character and N is the last digit of the year. + // + // Does expiration date string have the asset symbol in it? + if (strDate.StartsWith(Symbol)) + { + // YES: Eliminate the asset symbol + strDate = strDate.Replace(Symbol, ""); + + // The date string should now be only two characters + if (strDate.Length == 2) + { + // YES: Correct length. Get the month code and the year + monthCode = strDate[0].ToString(); + yearCode = Convert.ToInt32(strDate.Substring(1, 1)); + + // Is this a FUT or an FUTX? + if (AssetType.StartsWith("FUT")) + { + // YES: Expiration date for FUTURES. Third Fri. + // of every third month. + // + // Get the month index for the FUTURE expiration + monthIndex = FutureCodes.IndexOf(monthCode) + 1; + + // Get the FUTURES expiration date + futures.ExpirationDate = GetDateFromCode(yearCode, monthIndex); + } + else + { + // NO: For OPTIONS. For CALL? + if (CallCodes.Contains(monthCode)) + { + // YES: For a CALL + option.PutCallType = "C"; + + // Get the month index for the CALL expiration + monthIndex = CallCodes.IndexOf(monthCode) + 1; + } + else + { + // NO: For a PUT + option.PutCallType = "P"; + + // Get the month index for the PUT expiration + monthIndex = PutCodes.IndexOf(monthCode) + 1; + } + + // Get and save the option expiration date + option.ExpirationDate = GetDateFromCode(yearCode, monthIndex); + } + } + } + } + } + catch (Exception e) + { + + } + + // Return the asset symbol + return; + } + + //********************************************************************* + // Method: GetDateFromCode + // + /// + /// Return a date time from a year and month code. + /// + /// + /// + /// The year code of the date and time (the last digit of the year) + /// + /// + /// + /// The one character month code (different for PUTS, CALLS, and + /// FUTURES) + /// + /// + /// + /// A date time object. + /// + /// + /// + /// The expiration date, if not set explicitly, is set as the third + /// Friday of the month. + /// + //********************************************************************* + private DateTime + GetDateFromCode + ( + int yearCode, + int monthIndex + ) + { + // Method members + DateTime date; + int currentYear; + int currentYearDiv10; + int lastDigitYear; + + // Get the current year + currentYear = DateTime.UtcNow.Year; + + // Divide the current year by 10 + currentYearDiv10 = currentYear / 10; + + // Isolate last digit of current year + lastDigitYear = Math.Abs(currentYear - currentYearDiv10 * 10); + + // If the year code is less than the last digit + // of the current year, add one to the year + // divided by 10 + if (yearCode < lastDigitYear) ++currentYearDiv10; + + // Reformulate the expiration year + date = new DateTime(currentYearDiv10 * 10 + lastDigitYear, monthIndex, 1); + if (date.TryGetDayOfMonth(DayOfWeek.Friday, 3, out date)) + { + // Is the expiration date .lte one day from today? + if (date <= DateTime.UtcNow.AddDays(-1)) + { + // YES: Invalid expiration date + Valid = false; + } + else + { + // NO: Valid expiration date + option.ExpirationDate = date; + + // Set asset valid + Valid = true; + } + } + + // Return the computed date + return date; + } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/Trade.cs b/TDAmeritradeZorro/Classes/TDA/Trade.cs new file mode 100644 index 0000000..b415b46 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/Trade.cs @@ -0,0 +1,559 @@ +//***************************************************************************** +// File: Trade.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: A class that records trades initiated by the Zorro Engine and +// executed through the TD Ameritrade API. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Web.Script.Serialization; +using DBLib.Classes; +using TDAmeritradeZorro.Utilities; + +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // Class: Trades + // + /// + /// This class implements the functionality required to same and retrieve + /// trades that have been entered into TD Ameritrade. + /// + /// NOTE: The reason this class is required is because TD Ameritrade + /// returns a LONG trade number but Zorro only accepts an INT trade number. + /// Therefore, we need to put in-place a means of assigning an INT trade + /// number to any entered trade and keeping track of it. This is done thru + /// a JSON data file that is synchronized with a list of entered trades. + /// This class creates the file and the list and keeps the two in-sync. + /// + /// Could possibly have used an embedded Sqlite database to accomplish this + /// but wanted to reduce dependence on third-party DLL. Also, first tries + /// with Sqlite showed it may not play well with DLLExport. + /// + //************************************************************************* + public class Trade + { + #region CLASS MEMBERS + //********************************************************************* + // Member: sqlBylZorroId + // + /// + /// SQL statement for getting a trade record by its Zorro ID number. + /// + //********************************************************************* + private static readonly string sqlBylZorroId = + @"SELECT * FROM [Trade] WHERE ZorroTradeId = '{0}'"; + #endregion CLASS MEMBERS + + #region CLASS PROPERTIES + //********************************************************************* + // Property: Id + // + /// + /// Primary key, and auto-incremented id for database table record. + /// + //********************************************************************* + [PrimaryKey] + [AutoIncrement] + [NotNull] + public int Id { get; set; } + + //********************************************************************* + // Member: Asset + // + /// + /// The Zorro symbol for this asset. + /// + //********************************************************************* + [NotNull] + public string Asset { get; set; } + + //********************************************************************* + // Member: AssetTYpe + // + /// + /// The asset type (EQUITY, ETF, FOREX, etc) + /// + //********************************************************************* + [NotNull] + public string AssetType { get; set; } + + //********************************************************************* + // Member: OrderType + // + /// + /// The order type (MARKET, STOP, STOP_LIMIT, etc.) + /// + //********************************************************************* + [NotNull] + public string OrderType { get; set; } + + //********************************************************************* + // Member: Instruction + // + /// + /// Instruction (BUY or SELL) + /// + //********************************************************************* + [NotNull] + public string Instruction{ get; set; } + + //********************************************************************* + // Member: TDTradeId + // + /// + /// The trade id returned from TD Ameritrade. + /// + //********************************************************************* + [NotNull] + public long TDTradeId { get; set; } + + //********************************************************************* + // Member: ZorroTradeId + // + /// + /// The trade id assigned to Zorro. + /// + //********************************************************************* + [NotNull] + public int ZorroTradeId { get; set; } + + //********************************************************************* + // Member: Quantity + // + /// + /// The amount traded. + /// + //********************************************************************* + [NotNull] + public int Quantity { get; set; } + + //********************************************************************* + // Member: Price + // + /// + /// The price of the trade entered. + /// + //********************************************************************* + public double Price { get; set; } + + //********************************************************************* + // Member: Open + // + /// + /// The price of the asset, including spread, upon entering a trade. + /// Not available for NFA compliant accounts. Not availabe for NFA + /// compliant accounts. + /// + //********************************************************************* + public double Open { get; set; } + + //********************************************************************* + // Member: Close + // + /// + /// The price of the asset at end of trading. + /// + //********************************************************************* + public double Close { get; set; } + + //********************************************************************* + // Member: Cost + // + /// + /// The total rollover (swap) fee for a trade. Not available for NFA + /// compliant accounts. + /// + //********************************************************************* + public double Cost { get; set; } + + //********************************************************************* + // Property: Profit + // + /// + /// The profit or loss of the trade, so far. Not available for NFA + /// complient accounts: + /// + //********************************************************************* + public double Profit { get; set; } + + //********************************************************************* + // Member: Filled + // + /// + /// The amount filled. + /// + //********************************************************************* + public int Filled { get; set; } + + //********************************************************************* + // Member: Status + // + /// + /// Status of the trade. + /// + //********************************************************************* + public string Status { get; set; } + + //********************************************************************* + // Member: StatusCode + // + /// + /// StatusCode of the trade. + /// + //********************************************************************* + public int StatusCode { get; set; } + + //********************************************************************* + // Member: OrderJson + // + /// + /// The json payload for the order + /// + //********************************************************************* + [NotNull] + public string OrderJson { get; set; } + + //********************************************************************* + // Member: Entered + // + /// + /// The date the trade was entered. + /// + //********************************************************************* + [NotNull] + public DateTime Entered { get; set; } + #endregion CLASS PROPERTIES + + public Trade() + { + // Initialize class properties (doubles) + Price = Close = Cost = Profit = Open = 0.0; + + // Initialize class properties (ints) + ZorroTradeId = Quantity = Filled = StatusCode = 0; + + // Initialize class property (long) + TDTradeId = 0L; + + // Initialize class properties (strings) + Asset = AssetType = OrderType = Instruction = Status = string.Empty; + + // Initializse the date the trade was entered + Entered = DateTime.MinValue; + } + + #region PUBLIC METHODS + //********************************************************************* + // Method: Save + // + /// + /// Save a current trade and return the Zorro trade id. + /// + /// + /// + /// A Zorro trade id (integer) + /// + //********************************************************************* + public int + Save + () + { + try + { + // Get the next Zorro Trade Id to assign to this trade + ZorroTradeId = GetZorroTradeId(); + + // Does the record already exist? + if (GetTradeByZorroId(ZorroTradeId) == null) + { + // NO: Insert it. + DataAccess.Insert(this); + } + + // Return success code (Zorro trade id) + return ZorroTradeId; + } + catch(Exception e) + { + // Log the error + LogHelper.Log(LogLevel.Error, $"{Resx.GetString("ERROR")}: " + e.Message); + + // Return error code + return -1; + } + } + #endregion PUBLIC METHODS + + #region PUBLIC STATIC METHODS + //********************************************************************* + // Method: GetTradeByZorroId + // + /// + /// Given a Zorro trade id, get the trade entered into TD Ameritrade. + /// + /// + /// + /// Zorro trade id for the trade. + /// + /// + /// + /// Trade object with the given Zorro trade id. + /// + //********************************************************************* + public static Trade + GetTradeByZorroId + ( + int ZorroTradeId + ) + { + // Look-up the trade in the trades list from the Zorro Trade Id + List trades = DataAccess.GetRecordBySql( + string.Format(sqlBylZorroId, ZorroTradeId)); + + // Are there more than one record + if (trades.Count == 1) + { + // YES: Got the record. Return the first trade + return trades[0]; + } + else + { + // NO: Got no record. Return a dummy trade. + return null; + } + } + #endregion PUBLIC STATIC METHODS + + #region PRIVATE STATIC METHODS + //********************************************************************* + // Method: GetZorroTradeId + // + /// + /// Get the current autoincrment value. + /// + //********************************************************************* + private static int + GetZorroTradeId + () + { + int retId = -1; + + // Get the first entry, which is a dummy entry for holding the auto- + // increment value + TradeId tradeId = DataAccess.GetOrdinalRecord(1, ""); + + // Was the next trade id record found? + if (tradeId != null) + { + // YES: Bump the next trade Id number by one + retId = tradeId.NextZorroId++; + + // Update the trade id record + DataAccess.Update(tradeId); + } + else + { + // NO: Must be no records so, create a new record + tradeId = new TradeId(); + + // Give it a starting Zorro trade id + retId = 1000; + tradeId.NextZorroId = 1001; + + // Save it + DataAccess.Insert(tradeId); + } + + // Return a Zorro trade id. + return retId; + } + + //********************************************************************* + // Method: TDAm2ZorroSymbol + // + /// + /// Convert a symbol from a TD Asset symbol to a Zorro (IB) symbol + /// + /// + /// + /// TD Asset symbol + /// + /// + /// + /// Zorro (IB) asset symbol + /// + /// + /// + /// TD Ameritrade symbol is in form SSS_MMDDYYXNN.N, where: + /// + /// SSS is the ticker symbol + /// MMDDYY is the expiration date (if present) + /// X is PUT or CALL (if present) + /// NN.N is the strike price if present. + /// + /// An equivalent Zorro (IB) symbol is: + /// SSS-TTT-YYYYMMDD-NNN.N-X-EEEE, where: + /// + /// SSS is the ticker symbol + /// TTT is the asset type (STK, ETF, OPT) + /// YYYYMMDD is the expiration date + /// NN.N is the strike price + /// X is PUT or CALL + /// EEEEE is the exchange (SMART for TD Ameritrade) + /// + //********************************************************************* + public static string + TDAm2ZorroSymbol + ( + string tdSymbol + ) + { + // Method members + DateTime date; + double strikePrice; + string zSymbol = string.Empty; + + // Is the just a ticker symbol, retur it + if (!tdSymbol.Contains("_")) return tdSymbol; + + // Break apart the symbol + string[] parts = tdSymbol.Split('_'); + + // Has more than a ticker symbol with underscore it in. Only doing + // STOCKS, ETFs, and OPTIONS currently. If it were a STOCK or ETF + // it would have not gotten this far. Must be an OPT. + zSymbol = parts[0] + "-" + "OPT-"; + + // Get the date from the TD Ameritrade symbol + if (DateTime.TryParse($"{parts[1].Substring(0, 2)}/{parts[1].Substring(2, 2)}/{parts[1].Substring(4, 2)}", out date)) + { + // Successful parse, add date to symbol string + zSymbol += date.ToString("yyyyMMdd") + "-"; + + // Get strike price from TD Ameritrade symbol + if (Double.TryParse(parts[1].Substring(7), out strikePrice)) + { + // Successful parse of strike price, add to Zorro symbol + zSymbol += strikePrice.ToString("N2").Trim('0') + "-"; + + // Add the PUT or CALL + zSymbol += parts[1].Substring(6, 1) + "-NYSE"; + } + else + { + zSymbol = string.Empty; + } + } + else + { + zSymbol = string.Empty; + } + + // Return the Zorro symbol + return zSymbol; + } + + //********************************************************************* + // Method: Zorro2TDAmSymbol + // + /// + /// Convert a symbol from a Zorro (IB) to a TD Amenitrade symbol. + /// + /// + /// + /// The Zorro symbol + /// + /// + /// + /// TD Ameritrade symbol + /// + /// + /// + /// Zorro (IB) symbol is: + /// SSS-TTT-YYYYMMDD-NNN.N-X-EEEE, where: + /// + /// SSS is the ticker symbol + /// TTT is the asset type (STK, ETF, OPT) + /// YYYYMMDD is the expiration date + /// NN.N is the strike price + /// X is PUT or CALL + /// EEEEE is the exchange (SMART for TD Ameritrade) + /// + /// Equivalent TD Ameritrade symbol is in form SSS_MMDDYYXNN.N, where: + /// + /// SSS is the ticker symbol + /// MMDDYY is the expiration date (if present) + /// X is PUT or CALL (if present) + /// NN.N is the strike price if present. + /// + /// + //********************************************************************* + public static string + Zorro2TDAmSymbol + ( + string zSymbol + ) + { + // Create a TD Asset object, which will automatically parse the + // symbol + TDAsset tdAsset = new TDAsset(zSymbol); + OrderSubmission order = new OrderSubmission(tdAsset, 0, 0, 0, false); + + return order.GetOptionSymbol(); + } + //********************************************************************* + // Method: JsonCopy + // + /// + /// Make a non-referential clone of an trade + /// + /// + /// + /// Trade for which a clane is being made. + /// + /// + /// + /// An trade object which does not reference any other trade object, + /// i.e. a clone. + /// + //********************************************************************* + public static Trade + JsonCopy + ( + Trade trade + ) + { + // Create a new javascript serializer + JavaScriptSerializer jss = new JavaScriptSerializer(); + + // Serialize, then deserialize the original trade, to get the clone + return jss.Deserialize(jss.Serialize(trade)); + } + #endregion PRIVATE STATIC METHODS + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/TradeId.cs b/TDAmeritradeZorro/Classes/TDA/TradeId.cs new file mode 100644 index 0000000..80eecef --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/TradeId.cs @@ -0,0 +1,58 @@ +//***************************************************************************** +// File: TradeId.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: Class that holds the next Zorro trade id number. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using DBLib.Classes; + +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // Classe: TradeId + // + /// + /// A simple class that just holds the current Zorro Trade number, used for + /// assigning an INTEGER to a Zorro trade because TD Ameritrade assigns a + /// LONG to a trade id. + /// + //************************************************************************* + public class TradeId + { + //********************************************************************* + // Property: Id + // + /// + /// Primary key, and auto-incremented id for database table record. + /// + //********************************************************************* + [PrimaryKey] + [AutoIncrement] + [NotNull] + public int Id { get; set; } + + [NotNull] + public int NextZorroId { get; set; } + } +} diff --git a/TDAmeritradeZorro/Classes/TDA/TradeXref.cs b/TDAmeritradeZorro/Classes/TDA/TradeXref.cs new file mode 100644 index 0000000..29119d0 --- /dev/null +++ b/TDAmeritradeZorro/Classes/TDA/TradeXref.cs @@ -0,0 +1,100 @@ +//***************************************************************************** +// File: TradeXref.cs +// +// Author: Clyde W. Ford +// +// Date: April 24, 2020 +// +// Description: A trade cross-reference class. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System; +using DBLib.Classes; + +namespace TDAmeritradeZorro.Classes.TDA +{ + //************************************************************************* + // Class: TradeXref + // + /// + /// A cross reference file of trades. + /// + /// + /// + /// TD Ameritrade orders sometimes spawn supplemental orders. These supple- + /// mental orders are not accounted for by Zorro, meaning that orphan trades + /// can be created. It is important that whenever a main trade is processed, + /// any supplemental trades associated with this main trade are also process- + /// ed. This class, and its related database table, keeps track of all trades + /// associated with orders from the Zorro trading engine. + /// + //************************************************************************* + public class TradeXref + { + //********************************************************************* + // Property: Id + // + /// + /// Primary key, and auto-incremented id for database table record. + /// + //********************************************************************* + [PrimaryKey] + [AutoIncrement] + [NotNull] + public int Id { get; set; } + + //********************************************************************* + // Property: PrimaryTDAId + // + /// + /// The primary TD Ameritrade Id number for the original trade order + /// submitted by Zorro. + /// + //********************************************************************* + [NotNull] + public long PrimaryTDAId { get; set; } + + //********************************************************************* + // Property: SecondaryTDAId + // + /// + /// The secondary, or xref'ed, TD Ameritrade order number for a trade + /// spawned by submission of the primary order. + /// + //********************************************************************* + [NotNull] + public long SecondaryTDAId { get; set; } + + //********************************************************************* + // Property: DateEntered + // + /// + /// The date and time this trade order was entered. + /// + //********************************************************************* + [NotNull] + public DateTime DateEntered { get; set; } + + public TradeXref() + { + DateEntered = DateTime.UtcNow; + } + } +} diff --git a/TDAmeritradeZorro/Classes/Tests.cs b/TDAmeritradeZorro/Classes/Tests.cs new file mode 100644 index 0000000..0d3d7a1 --- /dev/null +++ b/TDAmeritradeZorro/Classes/Tests.cs @@ -0,0 +1,610 @@ +//***************************************************************************** +// File: Tests.cs +// +// Author: Clyde W. Ford +// +// Date: April 29, 2020 +// +// Description: A variety of self-diagnostic tests. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System; +using System.IO; +using System.Linq; +using TDAmeritradeZorro.Authentication; +using TDAmeritradeZorro.Authentication.Client; +using TDAmeritradeZorro.Authentication.Configuration; +using TDAmeritradeZorro.Classes.TDA; +using TDAmeritradeZorro.Structs; +using TDAmeritradeZorro.Utilities; + +namespace TDAmeritradeZorro.Classes +{ + //************************************************************************* + // Class: Tests + // + /// + /// This class supports testing of the methods and other components of this + /// plug-in. + /// + //************************************************************************* + public static class Tests + { + //********************************************************************* + // Method: DoTests + // + /// + /// Main method for performing self-diagnostic tests. + /// + /// + /// + /// The text entered into the user name textbox of Zorro (client id). + /// + /// + /// + /// True if all tests successful, false if any test fails. + /// + /// + /// + /// NOTE: These tests evaluate only the internal operation of the plug-in. + /// To evaluate communication between the Zorro trading engine and the + /// plug-in run Zorro in 'Real' mode and select the TDAmeritradeTest + /// script. + /// + //********************************************************************* + public static bool + DoTests + ( + string usn + ) + { + // Method members + bool retCode; + AuthToken token; + + // Demo mode information for the user + LogHelper.Log("\r\nDEMO MODE"); + LogHelper.Log("---- ----"); + LogHelper.Log($"{Resx.GetString("TEST_HEADING")}"); + + // Set the test mode + TDAmerAPI.TestMode = true; + + // Initialize the database tables + Broker.CreateDbTables(); + + // Set the VERBOSITY LEVEL + TDAmerAPI.verbosityLevel = Verbosity.Intermediate; + + //***************************************************************** + // + // T E S T # 1: S E T T I N G S F I L E + // + //***************************************************************** + LogHelper.Log($"\r\n{Resx.GetString("TEST")} #1:"); + LogHelper.Log($"{Resx.GetString("READING_SETTINGS_FILE")}...."); + Broker.settings = Settings.Read(); + + // Were settings obtained? + if (Broker.settings == null || + string.IsNullOrEmpty(Broker.settings.Currency) || + string.IsNullOrEmpty(Broker.settings.TdaAccountNum)) + { + // NO: Error has been logged, exit with failure code + LogHelper.Log($"{Resx.GetString("SETTINGS_FILE_ERROR")}...."); + return false; + } + else + { + LogHelper.Log($"{Resx.GetString("SUCCESS").ToUpper()}: {Resx.GetString("READING_SETTINGS_FILE")}."); + } + + //***************************************************************** + // + // T E S T # 2: U S E R A U T H E N T I C A T I O N + // + //***************************************************************** + LogHelper.Log($"\r\n{Resx.GetString("TEST")} #2:"); + LogHelper.Log($"{Resx.GetString("AUTHENTICATING_USER")}..."); + + // The username is actually the client id, save it in settings + Broker.settings.ClientId = usn; + + // Save the full path of the token data file + Broker.tokenDataFile = Broker.WORKING_DIR + Broker.dataFile; + + // Initialize the connection configuration using the refresh token + // (usn) and the client id obtained from the settings + Broker.oAuthConfiguration = new TDAmeritradeConnectConfiguration(Broker.settings.ClientId); + + // Initialize the connection client with the configuration + Broker.oAuthClient = new TDAmeritradeConnectClient(Broker.oAuthConfiguration); + + // Delete the token file + if (File.Exists(Broker.tokenDataFile)) File.Delete(Broker.tokenDataFile); + + // Get an authentication token + token = GetAuthToken(); + + // Auth token received? + if (token != null) + { + LogHelper.Log($"{Resx.GetString("SUCCESS").ToUpper()}: {Resx.GetString("TEST_AUTHENTICATION_AUTH_TOKEN")}"); + } + else + { + LogHelper.Log(LogLevel.Error, $"{Resx.GetString("FAILURE").ToUpper()}: {Resx.GetString("TEST_AUTHENTICATION_FAILURE")}."); + return false; + } + + //***************************************************************** + // + // T E S T # 3: R E F R E S H A U T H E N T I C A T I O N + // + //***************************************************************** + LogHelper.Log($"\r\n{Resx.GetString("TEST")} #3:"); + LogHelper.Log($"{Resx.GetString("TEST_REFRESH_TOKEN_AUTH")}..."); + + // Get another auth token, should be using refresh token now + token = GetAuthToken(); + + // Auth token received? + if (token != null) + { + LogHelper.Log($"{Resx.GetString("SUCCESS").ToUpper()}: {Resx.GetString("TEST_ACCESS_TOKEN_FROM_REFRESH")}."); + } + else + { + LogHelper.Log(LogLevel.Error, $"{Resx.GetString("SUCCESS").ToUpper()}: {Resx.GetString("TEST_NO_ACCESS_TOKEN_FROM_REFRESH")}."); + return false; + } + + //***************************************************************** + // + // T E S T # 4: M A R K E T H O U R S + // + //***************************************************************** + LogHelper.Log($"\r\n{Resx.GetString("TEST")} #4:"); + LogHelper.Log($"{Resx.GetString("GET_NYSE_HOURS")}...."); + + bool isNYSEOpen = Broker.IsMarketOpen("EQUITY"); + + // Print out results + string open = isNYSEOpen ? Resx.GetString("OPEN").ToUpper() : Resx.GetString("NOT_OPEN").ToUpper(); + LogHelper.Log($"{Resx.GetString("CURRENT_UTC_TIME")} {DateTime.UtcNow}."); + LogHelper.Log($"{Resx.GetString("THE_NYSE_IS")} {open}."); + + //***************************************************************** + // + // T E S T # 5: B R O K E R T I M E + // + //***************************************************************** + LogHelper.Log($"\r\n{Resx.GetString("TEST")} #5:"); + LogHelper.Log($"{Resx.GetString("BROKER_TIME")}..."); + double? dServerTime; + + // Call the Broker.Time function and get the server time out + int code = Broker.Time(out dServerTime); + + // Print the current time + LogHelper.Log($"{Resx.GetString("CURRENT_UTC_TIME")} {DateTime.UtcNow}."); + + // Evaluate return code from Broker.Time + if (code == 0) + { + LogHelper.Log($"{Resx.GetString("ERROR")}: {Resx.GetString("CONN_LOST")}."); + return false; + } + else if (code == 1) + { + LogHelper.Log($"{Resx.GetString("CONN_OK_MARKET_CLOSED")}."); + } + else + { + LogHelper.Log($"{Resx.GetString("CONN_OK_MARKET_OPEN")}."); + } + + LogHelper.Log($"{Resx.GetString("SERVER_UTC_OLE")}: {dServerTime}."); + LogHelper.Log($"{Resx.GetString("SERVER_UTC_CONVERTED")}: {DateTime.FromOADate((double)dServerTime)}."); + + // Get the server time and the current time + DateTime currentTime = DateTime.UtcNow; + DateTime serverTime = DateTime.FromOADate((double)dServerTime); + + // Get the difference in the two times + TimeSpan diffTime = serverTime - currentTime; + int secondsDiff = Math.Abs(diffTime.Seconds); + if (secondsDiff < 30) + { + LogHelper.Log($"{Resx.GetString("TEST_SERVER_TIME_SUCCESS")}."); + } + else + { + LogHelper.Log($"{Resx.GetString("TEST_SERVER_TIME_FAILURE")}."); + return false; + } + + //***************************************************************** + // + // T E S T # 6: A S S E T S U B S C R I P T I O N + // + //***************************************************************** + LogHelper.Log($"\r\n{Resx.GetString("TEST")} #6:"); + LogHelper.Log($"{Resx.GetString("BROKER_ASSET")}..."); + + // Use Microsoft + string symbol = "MSFT"; + TDAsset asset = Broker.Asset(symbol); + if (asset == null) + { + LogHelper.Log($"{Resx.GetString("TEST_ASSET_INFO_FAILURE")} {symbol}."); + return false; + } + + // Subscribe to this asset + Broker.Subscription(asset); + + // Is asset on subscription list + if (Broker.subscriptionList.Count > 0 && Broker.subscriptionList[0].Symbol == symbol) + { + LogHelper.Log($"{Resx.GetString("TEST_ASSET_SUCCESS")} {symbol}."); + } + else + { + LogHelper.Log($"{Resx.GetString("TEST_ASSET_FAILURE")} {symbol}."); + return false; + } + + //***************************************************************** + // + // T E S T # 7: U P D A T E A S S E T S C S V F I L E + // + //***************************************************************** + LogHelper.Log($"\r\n{Resx.GetString("TEST")} #7:"); + LogHelper.Log($"{Resx.GetString("UPDATING_ASSETS_CSV")}..."); + + // Update the assets CSV fileU + retCode = AssetsCSV.UpdateAssetsCSV(); + if (retCode) + { + LogHelper.Log($"{Resx.GetString("SUCCESS").ToUpper()}: {Resx.GetString("UPDATING_ASSETS_CSV")}."); + } + else + { + LogHelper.Log($"{Resx.GetString("FAILURE").ToUpper()}: {Resx.GetString("UPDATING_ASSETS_CSV")}."); + return false; + } + + //***************************************************************** + // + // T E S T # 8: T R A D E F I L E S Y N C H R O N I Z A T I O N + // + //***************************************************************** + LogHelper.Log($"\r\n{Resx.GetString("TEST")} #8:"); + LogHelper.Log($"{Resx.GetString("TRADE_FILE_SYNCHRONIZATION")}..."); + + // Update the assets CSV file + retCode = Broker.Sync(); + if (retCode) + { + LogHelper.Log($"{Resx.GetString("SUCCESS").ToUpper()}: {Resx.GetString("TRADE_FILE_SYNCHRONIZATION")}."); + } + else + { + LogHelper.Log($"{Resx.GetString("FAILURE").ToUpper()}: {Resx.GetString("TRADE_FILE_SYNCHRONIZATION")}."); + return false; + } + + + //***************************************************************** + // + // T E S T # 9: M A R K E T B U Y / S E L L + + //***************************************************************** + LogHelper.Log($"\r\n{Resx.GetString("TEST")} #9:"); + LogHelper.Log($"{Resx.GetString("TEST_BUY_SELL_INFO")}"); + + // The test symbol + symbol = "GRPN"; + + // Get information for + TDAsset tdAsset = Broker.Asset(symbol); + Trade order = null; + + // Was the information obtained for this asset? + if (tdAsset != null) + { + + // YES: Subscribe to this asset + Broker.Subscription(tdAsset); + + // Is the NYSE Open? + if (!Broker.IsMarketOpen("EQUITY")) + { + // NO: BUY 1 share of tdAsset AT MARKET + order = PlaceOrder(tdAsset.Symbol, 2, 0, 0); + if (!GetOrderInfo(order)) return false; + + // SELL 1 share of tdAsset AT MARKET + PlaceOrder(tdAsset.Symbol, -12, 0, 0); + if (!GetOrderInfo(order)) return false; + + } + + PlaceOrder(tdAsset.Symbol, 2, 1.50, 150); + + // Can always place LIMIT orders with outrageous limits so they can + // be canceled immediately. + + // BUY 1 share of tdAsset AT LIMIT price (100% above current price) + // Asset price should never get there, so order should pend and + // easily be canceled. + order = PlaceOrder(tdAsset.Symbol, 1, 0, tdAsset.Price * 2); + if (!GetOrderInfo(order)) return false; + + // SELL 1 share of tdAsset AT LIMIT price (100% above current + // price) Asset price should never get there, so order should pend + // and easily be canceled. + order = PlaceOrder(tdAsset.Symbol, -12, 0, tdAsset.Price * 2); + if (!GetOrderInfo(order)) return false; + } + else + { + // Log the error + LogHelper.Log($"{Resx.GetString("ERROR")}: {Resx.GetString("SUBSCRIBING_TO_ASSET")}."); + + // Return error status + return false; + } + + //***************************************************************** + // + // T E S T # 10: B R O K E R H I S T O R Y + // + //***************************************************************** + LogHelper.Log($"\r\n{Resx.GetString("TEST")} #10:"); + LogHelper.Log($"{Resx.GetString("TEST_HISTORICAL_INFO")}"); + + // Get the historical ticks for this asset + Tick[] ticks = Broker.History( + // Symbol + "MSFT", + + // Start date + DateTime.Parse("16 Feb 2019 14:00:00 GMT").ToOADate(), + + // End date + DateTime.Parse("05 May 2020 18:00:00 GMT").ToOADate(), + + // Number of ticks per minute + 60, + + // Maximum number of ticks + 300 + ); + + if (ticks.Count() == 300) + { + LogHelper.Log(LogLevel.Info, $"{Resx.GetString("SUCCESS").ToUpper()}: {Resx.GetString("MSFT_PRICE_HISTORY")}.") ; + } + else + { + LogHelper.Log(LogLevel.Error, $"{Resx.GetString("FAILURE").ToUpper()}: {Resx.GetString("MSFT_PRICE_HISTORY")}."); + return false; + } + + //***************************************************************** + // + // T E S T # 11: U S E R A C C O U N T + // + //***************************************************************** + LogHelper.Log($"\r\n{Resx.GetString("TEST")} #11:"); + LogHelper.Log($"{Resx.GetString("GET_USER_ACCT_INFO")}..."); + + AccountBalance bal = Broker.Account(Broker.settings.TdaAccountNum); + if (bal != null) + { + LogHelper.Log(LogLevel.Info, $"{Resx.GetString("SUCCESS").ToUpper()}: {Resx.GetString("RETRIEVE_ACCT_INFO")}{bal.AccountId}."); + } + else + { + LogHelper.Log(LogLevel.Error, $"{Resx.GetString("FAILURE").ToUpper()}: {Resx.GetString("RETRIEVE_ACCT_INFO")}{bal.AccountId}."); + return false; + } + + // If testing made it here, all tests were successful + return true; + } + + //********************************************************************* + // Method: GetOrderInfo + // + /// + /// Get order information for an order. + /// + /// + /// + /// Order to get information on. + /// + /// + /// + /// True if order information obtained successfully, false if not. + /// + //********************************************************************* + private static bool + GetOrderInfo + ( + Trade order + ) + { + // Was the order successful? + if (order.ZorroTradeId > 0) + { + order = Broker.BrokerTrade(order.ZorroTradeId); + if (order != null) + { + LogHelper.Log($"{Resx.GetString("TEST_ORDER_INFO_SUCCESS")} {order.Status}."); + return true; + } + else + { + LogHelper.Log(LogLevel.Error, $"{Resx.GetString("TEST_ORDER_INFO_FAILURE")}."); + return false; + } + } + else + { + return false; + } + } + + //********************************************************************* + // Method: PlaceOrder + // + /// + /// Place a trade order with TD Ameritrade using the REST API, then + /// immediately cancel that order. + /// + /// + /// + /// Ticker symbol to place order for. + /// + /// + /// + /// Number of shares. + /// + /// + /// + /// Stop distance. + /// + /// + /// + /// Limit price for LIMIT orders. + /// + /// + /// + /// Market session to trade at, TD Ameritrade only uses NORMAS for thu + /// REST API. + /// + /// + /// + /// True if order placed was a success AND the order was then canceled, + /// otherwise false. + /// + //********************************************************************* + private static Trade + PlaceOrder + ( + string symbol, + int amount, + double dStopDist, + double dLimit, + string session = "NORMAL" + ) + { + // Method members + bool retCode = false; + string saleType = (amount > 0) ? Resx.GetString("BUYING") : Resx.GetString("SELLING"); + string orderType = (dLimit > 0) ? Resx.GetString("LIMIT") + " " + Resx.GetString("PRICE_OF") + " " + dLimit.ToString("N4") : Resx.GetString("MARKET"); + + + // Log the attempted trade + LogHelper.Log($"\r\n{Resx.GetString("TRADE")}: {saleType} {Math.Abs(amount)} {Resx.GetString("SHARES_OF")} {symbol} {Resx.GetString("AT")} {orderType}."); + + // Place the order (force order at session) + Trade trade = Broker.Buy(symbol, amount, dStopDist, dLimit, session); + + // Log success or failure? + if (trade.TDTradeId > 0) + { + // Trade SUCCESS: + LogHelper.Log($"{Resx.GetString("SUCCESS").ToUpper()}: {Resx.GetString("PLACING_ORDER")} {symbol}."); + } + else + { + if (Broker.ComboLegs > 0) + { + // Trade WAITING + LogHelper.Log($"{Resx.GetString("WAITNG")}: {Resx.GetString("FOR_MORE_ORDER_LEGS")} {Resx.GetString("PLACING_ORDER")} {symbol}."); + } + else + { + // Trade FAILURE: + LogHelper.Log(LogLevel.Error, $"{Resx.GetString("FAILURE").ToUpper()}: {Resx.GetString("PLACING_ORDER")} {symbol}."); + } + } + + // Cancel the trade if it was made + if (trade.TDTradeId > 0) + { + retCode = Broker.Cancel(trade.ZorroTradeId); + if (retCode) + { + LogHelper.Log($"{Resx.GetString("SUCCESS").ToUpper()}: {Resx.GetString("CANCELING_ORDER")} {symbol}."); + } + else + { + LogHelper.Log($"{Resx.GetString("FAILURE").ToUpper()}: {Resx.GetString("CANCELING_ORDER")} {symbol}."); + } + } + + // Return the trade + return trade; + } + + //********************************************************************* + // Method: GetAuthToken + // + /// + /// Get an authorization token, necessary to use the REST API. + /// + /// + /// + /// An outhorization tokn, or null if that token can not be obtained. + /// + //********************************************************************* + private static AuthToken + GetAuthToken + () + { + // Method member + AuthToken token = null; + + // Does the token file exist? + if (File.Exists(Broker.tokenDataFile)) + { + // YES: Get an authentication token using a refresh token + token = AuthToken.GetAuthToken(); + } + else + { + // NO: This must be the initial use of the plug-in, in that + // case get an access token by fully authenticating user + // with the client id. + + // Execute the longer, one time only, user authentication. + token = AuthToken.AuthenticateUser(Broker.settings.ClientId); + } + + // Return the authentication token + return token; + } + } +} diff --git a/TDAmeritradeZorro/Classes/UserAccount.cs b/TDAmeritradeZorro/Classes/UserAccount.cs new file mode 100644 index 0000000..39e89a3 --- /dev/null +++ b/TDAmeritradeZorro/Classes/UserAccount.cs @@ -0,0 +1,55 @@ +//***************************************************************************** +// File: UserAccount.cs +// +// Author: Clyde W. Ford +// +// Date: April 29, 2020 +// +// Description: The TD Ameritrade user account class. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.Serialization; +using TDAmeritradeZorro.Classes.TDA; + +namespace TDAmeritradeZorro.Classes +{ + //************************************************************************* + // Class: UserAccount + // + /// + /// A class created after converting a JSON string to a C# UserAccount + /// object. + /// + //************************************************************************* + [DataContract] + public class UserAccount + { + //********************************************************************* + // Property: UserAccount + // + /// + /// All information for a particular JSON securitiesAccount object + /// returned from the TD Ameritrade API. + /// + //********************************************************************* + [DataMember(Name = "securitiesAccount")] + public SecuritiesAccount Account{ get; set; } + } +} diff --git a/TDAmeritradeZorro/Interfaces/IPrice.cs b/TDAmeritradeZorro/Interfaces/IPrice.cs new file mode 100644 index 0000000..e552d8f --- /dev/null +++ b/TDAmeritradeZorro/Interfaces/IPrice.cs @@ -0,0 +1,81 @@ +//***************************************************************************** +// File: IPrice.cs +// +// Author: Clyde W. Ford +// +// Date: April 29, 2020 +// +// Description: An interface for capturing price data on an asset +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +namespace TDAmeritradeZorro.Interface +{ + //************************************************************************* + // Interface: IPrice + // + /// + /// An interface for describing price data related to an asset. + /// + //************************************************************************* + public interface IPrice + { + //********************************************************************* + // Property: BidPrice + // + /// + /// The bid price of the asset. The highest price a buyer will pay for + /// the asset. + /// + //********************************************************************* + double BidPrice { get; set; } + + //********************************************************************* + // Property: AskPrice + // + /// + /// The ask price of the asset. The lowest price a seller will accept + /// for the asset. + /// + //********************************************************************* + double AskPrice { get; set; } + + //********************************************************************* + // Property: LastPrice + // + /// + /// The last price of the asset. The price of the last trade done for + /// the asset. + /// + //********************************************************************* + double LastPrice { get; set; } + + //********************************************************************* + // Property: ClosePrice + // + /// + /// The close price of the asset. The volume weighted average price of + /// all the trades that were done for an asset during the last half an + /// hour of the trading session, usually between 3:00 and 3:30 pm for + /// the NYSE. + /// + //********************************************************************* + double ClosePrice { get; set; } + } +} diff --git a/TDAmeritradeZorro/Lib/DBLib.dll b/TDAmeritradeZorro/Lib/DBLib.dll new file mode 100644 index 0000000..71c75bd Binary files /dev/null and b/TDAmeritradeZorro/Lib/DBLib.dll differ diff --git a/TDAmeritradeZorro/Lib/Microsoft.Data.Sqlite.dll b/TDAmeritradeZorro/Lib/Microsoft.Data.Sqlite.dll new file mode 100644 index 0000000..4c0a40f Binary files /dev/null and b/TDAmeritradeZorro/Lib/Microsoft.Data.Sqlite.dll differ diff --git a/TDAmeritradeZorro/Lib/TDAmeritrade.dll b/TDAmeritradeZorro/Lib/TDAmeritrade.dll new file mode 100644 index 0000000..0720ab6 Binary files /dev/null and b/TDAmeritradeZorro/Lib/TDAmeritrade.dll differ diff --git a/TDAmeritradeZorro/Lib/sqlite3.dll b/TDAmeritradeZorro/Lib/sqlite3.dll new file mode 100644 index 0000000..d5d1586 Binary files /dev/null and b/TDAmeritradeZorro/Lib/sqlite3.dll differ diff --git a/TDAmeritradeZorro/Properties/AssemblyInfo.cs b/TDAmeritradeZorro/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..2539b45 --- /dev/null +++ b/TDAmeritradeZorro/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("TDAmeritradeZorro")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("TDAmeritradeZorro")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5e3cb65b-feae-48c4-b16a-b25729626408")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/TDAmeritradeZorro/Resources/de.resx b/TDAmeritradeZorro/Resources/de.resx new file mode 100644 index 0000000..5c87048 --- /dev/null +++ b/TDAmeritradeZorro/Resources/de.resx @@ -0,0 +1,612 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Zugangstoken + + + Konto # + + + bereits abonniert + + + beim + + + Benutzer authentifizieren + + + Brokerkonto + + + Broker Asset + + + Broker Buy2 + + + Broker History2 + + + Broker Login + + + Broker offen + + + Maklerzeit + + + Maklerhandel + + + Kaufen + + + Kauf + + + Stornieren + + + abgebrochen + + + stornieren + + + Bestellung stornieren für + + + Währungszinssätze können nicht erhalten werden + + + geschlossen + + + Die Verbindung zu TD Ameritrade ist unterbrochen + + + NYSE GESCHLOSSEN + + + NYSE OPEN + + + Die aktuelle UTC-Zeit ist + + + Browser-Emulation aktivieren + + + Error + + + Beim Abrufen der Angebotsinformationen für ist ein nicht spezifizierter Fehler aufgetreten + + + FEHLER IN PLUG-IN-TESTS ERKANNT + + + Verlassen + + + Fehler + + + Datei + + + Datei I/O Fehler + + + Datei nicht gefunden + + + zum + + + gefunden + + + von + + + Holen Sie sich die NYSE Stunden + + + Position bekommen + + + Informationen zum Benutzerkonto abrufen + + + bekommen + + + Zugriffstoken erhalten + + + Neues Zugriffstoken erhalten + + + im + + + falsches Format + + + Information + + + Zinssätze initialisieren + + + Grenze + + + Liste + + + Laden und Synchronisieren der Handelsdatei + + + Laden der lokalen Handelsdatei + + + Laden der JSON-Handelsdatei + + + Markt + + + Marktzeiten + + + Abrufen historischer Preisdaten zu MSFT + + + Nettoposition für + + + Es wurde keine Bestellnummer für den Handel von zurückgegeben + + + Es wurden keine Zecken zurückgegeben + + + nicht ausgeführt + + + nicht gefunden + + + nicht offen + + + öffnen + + + Auftrag + + + Bestellung aufgeben für + + + Bestellung aufgeben für + + + Preisentwicklung + + + Preis von + + + lesen + + + SETTINGS-Datei lesen + + + Lesen der SETTINGS-Datei + + + Token aktualisieren + + + abgelehnt + + + Abrufen von Benutzerkontoinformationen für TDA # + + + Abrufen + + + Informationen abrufen für + + + Rückgabe Code + + + Verkaufen + + + Verkauf + + + Server-UTC-Zeit (konvertiert) + + + Server-UTC-Zeit (OLE) + + + die Einstellungen + + + und wiederholen Sie das Plug-In im Demo-Modus. + + + Aktien von + + + Status + + + Asset abonnieren + + + Erfolg + + + ALLE PLUG-IN-TESTS ERFOLGREICH + + + Synchronisieren + + + Synchronisieren der lokalen Handelsdatei mit TD Ameritrade-Geschäften. + + + TD Ameritrade + + + Prüfung + + + das vom Aktualisierungstoken erhalten wurde + + + FEHLER: Abonnieren + + + FEHLER: Abrufen von Asset-Informationen für + + + ERFOLG: Abonnieren + + + Benutzer authentifiziert. Auth-Token erhalten. + + + Benutzer nicht authentifiziert + + + Markt KAUFEN und VERKAUFEN. LIMIT KAUFEN UND VERKAUFEN ...\r\nWenn die NYSE GESCHLOSSEN ist, werden LIMIT- und MARKT-Bestellungen aufgegeben.\r\nWenn die NYSE geöffnet ist werden nur LIMIT-Bestellungen aufgegeben.\r\nLimit KAUFEN und VERKAUFEN werden zu Preisen platziert das sollte nicht getroffen werden.\r\nErfolgreiche Bestellungen werden sofort storniert. + + + Die TD Ameritrade-API unterstützt keinen "Papierhandel".\r\nIm DEMO-Modus durchläuft das Plug-In eine Reihe von\r\ndaselfostischen Tests. + + + 29. April 2020 18:00:00 GMT + + + sie aus dieser Quelle zu extrahieren.\r\n + + + das nicht vom Aktualisierungstoken erhalten wurde + + + FEHLER: Abrufen von Bestellinformationen + + + ERFOLG: Bestellinformationen abrufen.\r\nBestellstatus: + + + Aktualisieren Sie die Token-Authentifizierung + + + FEHLER: Serverzeit abweicht von der aktuellen Zeit + + + ERFOLG: Serverzeit erhalten + + + Die NYSE ist + + + Zecken kehrten für zurück + + + ABBRECHEN + + + SCHLIESSEN + + + Handel + + + Handelsliste + + + Handelslisten + + + Handel nicht in x-ref Liste gefunden + + + Unspezifizierter Fehler + + + Aktualisieren der CSV-Datei für Assets + + + Handel aktualisieren + + + Windows Form-Symbol nicht zerstört + + + Zorro + + + Sprache nicht gefunden (siehe Dokumentation) + + + Ungültige Sprachspezifikation (siehe Dokumentation) + + + Die Mindestanlage in diesen Investmentfonds wurde nicht erreicht + + + Ungültiges Asset nicht zur Abonnementliste hinzugefügt + + + Gültiges Asset zur Abonnementliste hinzugefügt + + + Combo-Beinreihenfolge fehlgeschlagen. Unterschiedliche LIMIT-Preise eingegeben + + + Combo-Beinreihenfolge fehlgeschlagen. Mixed LIMIT- und MARKET-Bestellungen sind nicht erlaubt + + + Für mehr Ordnung Beine + + + Optionskette abrufen + + + Kombioptionsreihenfolge speichern + + + Asset abonnieren + + + WARTEN + + + Optionskette abrufen + + + Akzeptiert + + + Warten auf einen Zustand + + + Warten auf manuelle Überprüfung + + + Warten auf Elternauftrag + + + Warten auf Outlet + + + Abgelaufen + + + Gefüllt + + + Ausstehende Aktivierung + + + Ausstehende Stornierung + + + Ausstehender Ersatz + + + In Warteschlange + + + Ersetzt + + + Arbeiten + + + Alle Aufträge zum Abschluss waren erfolgreich + + + Aber es konnte nicht abgebrochen werden + + + Es wurde abgesagt + + + Instrumente dürfen nicht in Combo-Leg-Ordnungen verwendet werden.\r\nNur OPTION-Befehle dürfen in Combo-Legs platziert werden + + + Keine LIMIT-Bestellungen innerhalb einer mehrbeinigen Bestellung + + + Bein bestellen + + + Einige Beine dieses kombinierten Beinhandels wurden nicht geschlossen + + + Dieser Auftrag zum Schließen war erfolgreich + + + Dieser Handel war + + + Dieser Handel wurde nicht geschlossen + + + Handel nicht geschlossen, weil Handel war + + + Asset-Liste abrufen + + + Test-Assets abrufen + + + Lizenz nicht akzeptiert, Plug-In kann nicht verwendet werden + + + Markt ist nicht geöffnet + + + nicht in Abonnementliste gefunden + + + Datenbanktabellen konnten nicht erstellt werden + + + Synchronisierung von Handelsdateien + + \ No newline at end of file diff --git a/TDAmeritradeZorro/Resources/en-US.resx b/TDAmeritradeZorro/Resources/en-US.resx new file mode 100644 index 0000000..882ec94 --- /dev/null +++ b/TDAmeritradeZorro/Resources/en-US.resx @@ -0,0 +1,612 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Accepted + + + Access Token + + + Account # + + + All orders to close were successful + + + already subscribed to + + + at + + + Authenticating user + + + Awaiting A Condition + + + Awaiting Manual Review + + + Awaiting Parent Order + + + Awaiting Outlet + + + Broker Account + + + Broker Asset + + + Broker Buy2 + + + Broker History2 + + + Broker Login + + + Broker Open + + + Broker Time + + + Broker Trade + + + But it could not be CANCELED + + + Buy + + + Buying + + + Cancel + + + canceled + + + canceling + + + Canceling order for + + + Currency interest rates cannot be obtained + + + closed + + + Combo leg order failed. Different LIMIT prices entered + + + Combo leg order failed. Mixed LIMIT and MARKET orders not allowed + + + Connection to TD Ameritrade is lost + + + Connection to TD Ameritrade OK, NYSE CLOSED + + + Connection to TD Ameritrade OK, NYSE OPEN + + + The current UTC time is + + + Enabling browser emulation + + + Error + + + ERRORS DETECTED IN PLUG-IN TESTS + + + Unspecificied error occurred retrieving quote information for + + + Exiting + + + Expired + + + Failure + + + file + + + File i/0 error + + + file not found + + + Filled + + + for + + + For more order legs + + + found + + + from + + + The minimum investment on this mutual fund has not been met + + + getting + + + Getting access token + + + Getting new access token + + + Get asset list + + + Get the NYSE hours + + + Get option chain + + + Get position + + + Getting test assets + + + Get user account information + + + in + + + incorrect format + + + information + + + Initializing interest rates + + + Invalid asset not added to subscription list + + + It was CANCELED + + + Language not found (see documentation) + + + Invalid language specification (see documentation) + + + License not accepted, plug-in cannot be used + + + Limit + + + list + + + Loading local trades file + + + Loading the JSON trades file + + + Loading and synchronization of trades file + + + Market + + + Market hours + + + Market is not open + + + Retrieving historical price data on MSFT + + + Net position for + + + not executed + + + not found + + + not found in subscription list + + + instruments may not be go in combo leg orders.\r\n Only OPTION orders may be placed in combo legs + + + not open + + + No Order # returned for trading of + + + No LIMIT orders within a multi-leg order + + + No ticks returned for + + + open + + + order + + + Order leg + + + Pending Activation + + + Pending Cancellation + + + Pending Replacement + + + Placing order for + + + Placing order for + + + price history + + + price of + + + Queued + + + Reading + + + Reading SETTINGS file + + + Reading the SETTINGS file + + + Refresh Token + + + rejected + + + Replaced + + + Retrieving user account information for TDA # + + + Retrieving + + + Retrieving information for + + + Retrieving option chain + + + Return code + + + Saving combo option order + + + Sell + + + Selling + + + Server UTC time (Converted) + + + Server UTC time (OLE) + + + Settings + + + FAILURE: Reading SETTINGS file.\r\nThe SETTINGS file should contain:\r\n (1) The Account Currency\r\n (2) The TD Ameritrade Acct. #\r\n (3) The default globalization language code\r\nPlease consult the documentation for this\r\nfile, the retry the plug-in in Demo mode. + + + shares of + + + Some legs of this combined leg trade were not closed + + + Status + + + Subscribing to asset + + + Subscribing to asset + + + Success + + + ALL PLUG-IN TESTS SUCCESSFUL + + + Synchronizing + + + Syncing local trades file with TD Ameritrade trades. + + + Database tables could not be created + + + TD Ameritrade + + + Test + + + Access token obtained from refresh token + + + FAILURE: Subscribing to + + + FAILURE: Getting asset information for + + + SUCCESS: Subscribing to + + + User authenticated. Auth token obtained. + + + User not authenticated + + + Market BUY and SELL. LIMIT BUY and SELL...\r\nIf the NYSE is CLOSED, LIMIT and MARKET orders will be placed.\r\nIf the NYSE is OPEN, only LIMIT orders will be placed.\r\nLimit BUY and SELL placed at prices that should not be hit.\r\nSuccessful orders will be canceled immediately. + + + The TD Ameritrade API does not support 'paper trading,' \r\nIn DEMO mode, the plug-in runs through a variety of\r\nself-daignostic tests. + + + Get historical information on MSFT...\r\nStart Time: Wed, 29 Apr 2020 14:00:00 GMT\r\n End Time: Wed, 29 Apr 2020 18:00:00 GMT + + + FAILURE: Currency interest rates not initialized.\r\nCurrency interest rates are obtained from the\r\nTrading Economics website. Check that this website's\r\naddress is: https://tradingeconomics.com/ and that an\r\ninterest rate table appears on this page. If not, the\r\nsource for international currency interest rates will\r\nneed to be changed, and the method of extracting them\r\nfrom that source as well.\r\n + + + Access token not obtained from refresh token + + + FAILURE: Retrieving order information + + + SUCCESS: Retrieving order information.\r\n Order Status: + + + Refresh token authentication + + + FAILURE: Server time various from current time + + + SUCCESS: Server time obtained + + + The NYSE is + + + This order to close was successful + + + This trade was + + + Ticks returned for + + + TO CANCEL + + + TO CLOSE + + + Trade + + + Trade file synchronization + + + trade list + + + trade lists + + + This trade was not closed + + + Trade not closed because trade was + + + Trade not found in x-ref list + + + Unspecified error + + + Updating the assets CSV file + + + Updating trade + + + Valid asset added to subscription list + + + WAITING + + + Windows Form icon not destroyed + + + Working + + + Zorro + + \ No newline at end of file diff --git a/TDAmeritradeZorro/Resources/es.resx b/TDAmeritradeZorro/Resources/es.resx new file mode 100644 index 0000000..414b652 --- /dev/null +++ b/TDAmeritradeZorro/Resources/es.resx @@ -0,0 +1,612 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Token de acceso + + + Cuenta # + + + ya suscrito a + + + a + + + Autenticando usuario + + + Cuenta de corredor + + + Agente de bienes + + + Broker Buy2 + + + Broker History2 + + + Inicio de sesión de corredor + + + Broker Open + + + Tiempo de corredor + + + Broker Trade + + + Comprar + + + Comprando + + + Cancelar + + + cancelado + + + cancelado + + + Cancelar orden para + + + No se pueden obtener tasas de interés de divisas. + + + cerrado + + + Se ha perdido la conexión con TD Ameritrade + + + NYSE CERRADO + + + NYSE ABIERTO + + + La hora actual UTC es + + + Habilitar la emulación del navegador + + + Error + + + Se produjo un error no especificado al recuperar la información de cotización para + + + ERRORES DETECTADOS EN PRUEBAS ENCHUFABLES + + + Salir + + + Fracaso + + + archivo + + + Error de archivo I/O + + + archivo no encontrado + + + para + + + encontró + + + desde + + + Obtenga las horas de NYSE + + + Obtener posición + + + Obtener información de la cuenta del usuario + + + consiguiendo + + + Obtener token de acceso + + + Obteniendo nuevo token de acceso + + + en + + + formato incorrecto + + + información + + + Inicializando tasas de interés + + + Idioma no encontrado (ver documentación) + + + Especificación de lenguaje no válida (ver documentación) + + + Límite + + + lista + + + Carga y sincronización de archivo de oficios + + + Cargando archivo de comercios locales + + + Cargando el archivo de operaciones JSON + + + Mercado + + + Horas de mercado + + + Recuperando datos históricos de precios en MSFT + + + Posición neta para + + + No se devuelve ningún pedido para el comercio de + + + No se devolvieron las garrapatas para + + + sin ejecutar + + + extraviado + + + no abierto + + + abierto + + + orden + + + Poner orden para + + + Poner orden para + + + historial de precios + + + precio de + + + Leyendo + + + Lectura del archivo de CONFIGURACIÓN + + + Leer el archivo de configuración + + + Actualizar token + + + rechazado + + + Recuperando información de cuenta de usuario para TDA # + + + Recuperando + + + Recuperando información para + + + Código de retorno + + + Vender + + + De venta + + + Hora UTC del servidor (convertida) + + + Hora UTC del servidor (OLE) + + + Configuraciones + + + vuelva a intentar el complemento en modo Demo. + + + acciones de + + + Estado + + + Suscribirse al activo + + + Éxito + + + TODAS LAS PRUEBAS ENCHUFABLES SON EXITOSAS + + + Sincronizando + + + Sincronizar el archivo de operaciones locales con las operaciones de TD Ameritrade. + + + TD Ameritrade + + + Prueba + + + Token de acceso obtenido del token de actualización + + + FALLA: Suscribirse a + + + FALLA: Obtener información de activos para + + + ÉXITO: ​​Suscribirse a + + + Usuario autenticado. Token de autenticación obtenido. + + + Usuario no autenticado + + + solo se realizarán LÍMITES de pedidos.\r\nLímite de COMPRA y VENTA a precios eso no debe ser golpeado.\r\nLas órdenes exitosas serán canceladas inmediatamente. + + + el complemento se ejecuta a través de una variedad de pruebas de\r\nself-daignostic. + + + 29 de abril de 2020 18:00:00 GMT + + + y también el método para extraerlas de esa fuente.\r\n + + + Token de acceso no obtenido del token de actualización + + + FALLA: Recuperando información del pedido + + + ÉXITO: ​​Recuperando información del pedido.\r\nEstado del pedido: + + + Actualizar autenticación de token + + + FALLO: hora del servidor diferente de la hora actual + + + ÉXITO: ​​tiempo de servidor obtenido + + + El NYSE es + + + Garrapatas devueltas para + + + CANCELAR + + + CERRAR + + + Comercio + + + lista de comercio + + + listas de comercio + + + Comercio no encontrado en la lista x-ref + + + Error no especificado + + + Actualización del archivo CSV de activos + + + Actualizando comercio + + + El icono de Windows Form no se destruye + + + Zorro + + + La inversión mínima en este fondo mutuo no se ha cumplido + + + Activo no válido no agregado a la lista de suscripción + + + Activo válido agregado a la lista de suscripción + + + Orden de pierna combinada fallida. Diferentes precios LIMIT ingresados + + + Orden de pierna combinada fallida. Órdenes mixtas LIMIT y MARKET no permitidas + + + Para más piernas de orden + + + Obtener cadena de opciones + + + Guardar orden de opción combinada + + + Suscribirse al activo + + + ESPERANDO + + + Recuperando la cadena de opciones + + + Aceptado + + + En espera de una condición + + + En espera de revisión manual + + + En espera de la orden de los padres + + + En espera de salida + + + Caducado + + + Lleno + + + Activacion Pendiente + + + Cancelación pendiente + + + Reemplazo pendiente + + + Puesto en cola + + + Reemplazado + + + Trabajando + + + Todas las órdenes de cierre fueron exitosas + + + Pero no se pudo CANCELAR + + + Fue cancelado + + + los instrumentos no pueden ir en órdenes de combo.\r\nSolo las órdenes de OPCIÓN pueden colocarse en combo. + + + No hay pedidos LIMIT dentro de un pedido de varias etapas + + + Orden de la pierna + + + Algunas piernas de este comercio combinado de piernas no estaban cerradas + + + Esta orden de cierre fue exitosa + + + Este comercio fue + + + Este comercio no fue cerrado + + + Comercio no cerrado porque el comercio era + + + Obtener lista de activos + + + Obteniendo activos de prueba + + + Licencia no aceptada, no se puede usar el complemento + + + El mercado no está abierto. + + + no encontrado en la lista de suscripción + + + No se pudieron crear tablas de base de datos + + + Sincronización de archivos comerciales + + \ No newline at end of file diff --git a/TDAmeritradeZorro/Resources/fr.resx b/TDAmeritradeZorro/Resources/fr.resx new file mode 100644 index 0000000..4d46fbb --- /dev/null +++ b/TDAmeritradeZorro/Resources/fr.resx @@ -0,0 +1,612 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Jeton d'accès + + + Compte # + + + déjà abonné à + + + à + + + Authentification de l'utilisateur + + + Compte de courtier + + + Actif de courtier + + + Courtier Buy2 + + + Historique du courtier2 + + + Connexion courtier + + + Courtier ouvert + + + Temps du courtier + + + Commerce de courtier + + + Acheter + + + Achat + + + Annuler + + + annulé + + + annulation + + + Annulation de la commande de + + + Impossible d'obtenir les taux d'intérêt sur les devises + + + fermé + + + La connexion à TD Ameritrade est perdue + + + NYSE FERME + + + NYSE OPEN + + + L'heure UTC actuelle est + + + Activation de l'émulation du navigateur + + + Erreur + + + Une erreur non spécifiée s'est produite lors de la récupération des informations de devis pour + + + ERREURS DÉTECTÉES DANS LES ESSAIS DE PLUG-IN + + + Quitter + + + Échec + + + fichier + + + Erreur de fichier I/O + + + Fichier non trouvé + + + pour + + + a trouvé + + + de + + + Obtenez les heures de NYSE + + + Obtenir une position + + + Obtenir des informations sur le compte utilisateur + + + avoir + + + Obtenir un jeton d'accès + + + Obtenir un nouveau jeton d'accès + + + dans + + + format incorrect + + + information + + + Initialisation des taux d'intérêt + + + Limite + + + liste + + + Chargement et synchronisation du fichier des métiers + + + Chargement du fichier des métiers locaux + + + Chargement du fichier métiers JSON + + + Marché + + + Heures de marché + + + Récupération des données de prix historiques sur MSFT + + + Position nette pour + + + Aucun ordre retourné pour échange de + + + Aucune tique retournée pour + + + Non exécuté + + + pas trouvé + + + pas ouverte + + + ouvert + + + ordre + + + Passer une commande pour + + + Passer une commande pour + + + historique des prix + + + prix de + + + En train de lire + + + Lecture du fichier SETTINGS + + + Lecture du fichier SETTINGS + + + Actualiser le jeton + + + rejeté + + + Récupération des informations de compte d'utilisateur pour TDA # + + + Récupération + + + Récupération d'informations pour + + + Code de retour + + + Vendre + + + Vente + + + Heure UTC du serveur (convertie) + + + Heure UTC du serveur (OLE) + + + Réglages + + + réessayez le plug-in en mode Démo. + + + parts de + + + Statut + + + Abonnement à l'actif + + + r + + + TOUS LES TESTS DE PLUG-IN SONT RÉUSSIS + + + Synchronisation + + + Synchronisation du fichier des transactions locales avec les transactions TD Ameritrade. + + + TD Ameritrade + + + Tester + + + Jeton d'accès obtenu à partir du jeton d'actualisation + + + ÉCHEC: abonnement à + + + ÉCHEC: obtenir des informations sur les actifs pour + + + SUCCÈS: S'abonner à + + + Utilisateur authentifié. Jeton d'authentification obtenu. + + + Utilisateur non authentifié + + + seules les commandes LIMITES seront passées.\r\nLimite ACHAT et VENTE passées aux prix cela ne devrait pas être atteint.\r\nLes commandes réussies seront annulées immédiatement. + + + le plug-in exécute une variété de\r\ntests auto-daignostiques. + + + 29 avr 2020 18:00:00 GMT + + + la\r\nsource des taux d'intérêt en devises internationales devra\r\nêtre modifiée et la méthode pour les extraire\r\nde cette source également.\r\n + + + Jeton d'accès non obtenu à partir du jeton d'actualisation + + + ÉCHEC: récupération des informations de commande + + + RÉUSSITE: récupération des informations de commande.\r\nÉtat de la commande: + + + Actualiser l'authentification par jeton + + + ÉCHEC: heure du serveur différente de l'heure actuelle + + + SUCCÈS: Temps serveur obtenu + + + Le NYSE est + + + Tiques retournées pour + + + ANNULER + + + FERMER + + + Commerce + + + liste de commerce + + + listes commerciales + + + Commerce non trouvé dans la liste x-ref + + + Erreur non spécifiée + + + Mise à jour du fichier CSV des actifs + + + Mise à jour du commerce + + + L'icône Windows Form n'est pas détruite + + + Zorro + + + Langue non trouvée (voir documentation) + + + Spécification de langue non valide (voir la documentation) + + + L'investissement minimum sur ce fonds commun de placement n'a pas été atteint + + + Élément non valide non ajouté à la liste d'abonnement + + + Élément valide ajouté à la liste d'abonnement + + + L'ordre de l'étape combinée a échoué. Différents prix LIMIT saisis + + + L'ordre de l'étape combinée a échoué. Ordres mixtes LIMIT et MARKET non autorisés + + + Pour plus de jambes d'ordre + + + Obtenir la chaîne d'options + + + Enregistrement de l'option de combo + + + Abonnement à l'actif + + + ATTENDRE + + + Récupération de la chaîne d'options + + + Accepté + + + En attente d'une condition + + + En attente de révision manuelle + + + En attente de l'ordre des parents + + + En attente de sortie + + + Expiré + + + Rempli + + + Activation en attente + + + Annulation en attente + + + Remplacement en attente + + + En file d'attente + + + Remplacé + + + Travail + + + Toutes les commandes de fermeture ont abouti + + + Mais cela ne pouvait pas être ANNULÉ + + + Il a été ANNULÉ + + + les instruments ne doivent pas être placés dans les commandes de jambes combinées.\r\n Seules les commandes OPTION peuvent être placées dans les jambes de combinaisons + + + Aucune commande LIMIT dans une commande multi-étapes + + + Ordre jambe + + + Certaines branches de ce commerce combiné n'ont pas été fermées + + + Cette commande a été conclue avec succès + + + Ce commerce était + + + Ce commerce n'a pas été fermé + + + Le commerce n'a pas été fermé parce que le commerce était + + + Obtenir la liste des actifs + + + Obtenir des actifs de test + + + Licence non acceptée, le plug-in ne peut pas être utilisé + + + Le marché n'est pas ouvert + + + introuvable dans la liste d'abonnement + + + Impossible de créer des tables de base de données + + + Synchronisation des fichiers commerciaux + + \ No newline at end of file diff --git a/TDAmeritradeZorro/Structs/CONTRACT.cs b/TDAmeritradeZorro/Structs/CONTRACT.cs new file mode 100644 index 0000000..179fbef --- /dev/null +++ b/TDAmeritradeZorro/Structs/CONTRACT.cs @@ -0,0 +1,156 @@ +//***************************************************************************** +// File: CONTRACT.cs +// +// Author: Clyde W. Ford +// +// Date: April 29, 2020 +// +// Description: CONTRACT structure for reporting an option chain. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.InteropServices; +namespace TDAmeritradeZorro.Structs +{ + //************************************************************************* + // Struct: CONTRACT + // + /// + /// Structure used to report the results of a query of an option chain for + /// a given symbol. + /// + /// + /// + /// This structure is used by Zorro's GET_OPTIONS Broker Command. + /// + //************************************************************************* + [StructLayout(LayoutKind.Sequential)] + public struct CONTRACT + { + //********************************************************************* + // Member: time + // + /// + /// The timestamp of the price information in UTC time given as an OLE + /// time value (i.e. a double). + /// + //********************************************************************* + public double time; + + //********************************************************************* + // Member: fAsk + // + /// + /// The ask price of the asset during the time period used. + /// + //********************************************************************* + public float fAsk; + + //********************************************************************* + // Member: fBid + // + /// + /// The bid price of the asset during the time period used. + /// + //********************************************************************* + public float fBid; + + //********************************************************************* + // Member: fVal + // + /// + /// The time value of the asset. + /// + //********************************************************************* + public float fVal; + + //********************************************************************* + // Member: fVol + // + /// + /// The volume of the asset during the time period used. + /// + //********************************************************************* + public float fVol; + + //********************************************************************* + // Member: fUnl + // + /// + /// The price of the underlying asset + /// + //********************************************************************* + public float fUnl; + + //********************************************************************* + // Member: fStrike + // + /// + /// The strike price of the underlying asset + /// + //********************************************************************* + public float fStrike; + + //********************************************************************* + // Member: Expiry + // + /// + /// The expiration date of the contract + /// + /// + /// + /// Long value but in form of YYYYMMDD + /// + //********************************************************************* + public long Expiry; + + //********************************************************************* + // Member: Type + // + /// + /// The type of contract (PUT, CALL, FUTURE, EUROPEAN, BINARY, etc.) + /// + //********************************************************************* + public long Type; + + public CONTRACT + ( + double dTime, + float ask, + float bid, + float val, + float vol, + float unl, + float strike, + long expiry, + long type + ) + { + time = dTime; + fAsk = ask; + fBid = bid; + fVal = val; + fVol = vol; + fUnl = unl; + fStrike = strike; + Expiry = expiry; + Type = type; + } + } +} diff --git a/TDAmeritradeZorro/Structs/Tick.cs b/TDAmeritradeZorro/Structs/Tick.cs new file mode 100644 index 0000000..1213f8b --- /dev/null +++ b/TDAmeritradeZorro/Structs/Tick.cs @@ -0,0 +1,133 @@ +//***************************************************************************** +// File: Tick.cs +// +// Author: Clyde W. Ford +// +// Date: April 29, 2020 +// +// Description: Tick structure for reporting OHLC data on an asset. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System.Runtime.InteropServices; +namespace TDAmeritradeZorro.Structs +{ + //************************************************************************* + // Struct: Tick + // + /// + /// Structure used to report the results of a query from the price history + /// of a given asset. + /// + /// + /// + /// This structure is used by Zorro's "BrokerHistory" method. Essentially, + /// a candlestick is returned (i.e. Open, High, Low, Close) along with the + /// timestamp of the price in OLE format. + /// + //************************************************************************* + [StructLayout(LayoutKind.Sequential)] + public struct Tick + { + //********************************************************************* + // Member: time + // + /// + /// The timestamp of the price information in UTC time given as an OLE + /// time value (i.e. a double). + /// + //********************************************************************* + public double time; + + //********************************************************************* + // Member: fHigh + // + /// + /// The high price of the asset during the time period used. + /// + //********************************************************************* + public float fHigh; + + //********************************************************************* + // Member: fLow + // + /// + /// The low price of the asset during the time period used. + /// + //********************************************************************* + public float fLow; + + //********************************************************************* + // Member: fOpen + // + /// + /// The opening price of the asset. + /// + //********************************************************************* + public float fOpen; + + //********************************************************************* + // Member: fClose + // + /// + /// The closing price of the asset. + /// + //********************************************************************* + public float fClose; + + //********************************************************************* + // Member: fVal + // + /// + /// The value of the asset during the time period used. + /// + //********************************************************************* + public float fVal; + + //********************************************************************* + // Member: fVol + // + /// + /// The volume of the asset during the time period used. + /// + //********************************************************************* + public float fVol; + + public Tick + ( + double dTime, + float open, + float close, + float high, + float low, + float val, + float vol + ) + { + time = dTime; + fOpen = open; + fClose = close; + fHigh = high; + fLow = low; + fVal = val; + fVol = vol; + } + } +} + diff --git a/TDAmeritradeZorro/TDAmerAPI.cs b/TDAmeritradeZorro/TDAmerAPI.cs new file mode 100644 index 0000000..0cf00f9 --- /dev/null +++ b/TDAmeritradeZorro/TDAmerAPI.cs @@ -0,0 +1,1522 @@ +//***************************************************************************** +// File: TDAmerAPI.cs +// +// Author: Clyde W. Ford +// +// Date: April 29, 2020 +// +// Description: A broker plug-in for Zorro that allows the Zorro trading +// engine to use TD Ameritrade as a broker through the TD Ameritrade API. See +// the ReadMe file and other documentation for important information prior to +// deploying this plug-in. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// Notes: There a two related NuGet packages for DLLExport that have similar +// namespaces and methods: UnmanagedExports and DLLExpart. DO NOT attempt to +// compile this plug-in with UnmanagedExports. The namespaces and methods may +// resolve but you will receive unresolvable compilation and runtime errors. +// USE ONLY DLLExport (https://github.com/3F/DllExport) available as a NuGet +// package to compile this plug-in. +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using TDAmer; +using TDAmeritradeZorro.Classes; +using TDAmeritradeZorro.Classes.TDA; +using TDAmeritradeZorro.Structs; +using TDAmeritradeZorro.Utilities; + +namespace TDAmeritradeZorro +{ + //************************************************************************* + // Class: TDAmerAPI + // + /// + /// The class implements the functionality of the Zorro calls to a plug-in. + /// Although Zorro expects a 32-bit plug-in with typical C++ calling de- + /// clarations, this plug-in uses the DllExport Library to achieve a 32- + /// bit plug-in that exposes the broker functions which can be called by + /// Zorro. The DLL Export Library can be found at: + /// + /// https://github.com/3F/DllExport/releases + /// + /// NOTE: There is another DllExport library by Robert Giesecke, entitled + /// UnmanagedExports which has the similar namespaces and similar methods. + /// DO NOT USE this DLL only use the first one referenced above. + /// + /// Some C# implementations of Broker plugins use double arrays as output + /// arguments for the double pointers passed from Zorro. In general, this + /// will work because the first element of an array is also the address of + /// the pointer to that array. The problem is that Zorro sometimes passes + /// a null pointer. Using a managed code array will not be able to detect + /// a null pointer, and will result in an error when a double value is + /// stored at that location. This plug-in accepts all Zorro output para- + /// meters as IntPtrs which are then tested for being null and converted + /// to double pointers in an UNSAFE code block. Thus, this plug-in should + /// always be built with the 'unsafe' method checked. + /// + //************************************************************************* + public class TDAmerAPI + { + #region DELEGATES + //********************************************************************* + // Delegate: BrokerErrorDelegate + // + /// + /// A delegate used to allow the BrokerError method to write to the + /// Zorro window. + /// + //********************************************************************* + public delegate int BrokerErrorDelegate(string txt); + + //********************************************************************* + // Delegate: BrokerProgressDelegate + // + /// + /// A delegate used to allow the BrokerProgress method to show a + /// progress indicator in the Zorro window. + /// + //********************************************************************* + public delegate int BrokerProgressDelegate(int percent); + #endregion DELEGATES + + #region PUBLIC MEMBERS + //********************************************************************* + // Delegate: BrokerError + // + /// + /// Reference to the method that writes to the Zorro window. This + /// method made public so it can be called in methods outside of this + /// class. + /// + //********************************************************************* + public static BrokerErrorDelegate BrokerError; + + //********************************************************************* + // Delegate: opMode + // + /// + /// The operating mode of the plug-in (Demo or Real). + /// + //********************************************************************* + public static OpMode opMode; + #endregion PUBLIC MEMBERS + + #region PRIVATE MEMBERS + //********************************************************************* + // Delegate: BrokerProgress + // + /// + /// Reference to the method that shows a progress indicator on the + /// Zorro window. + /// + //********************************************************************* + private static BrokerProgressDelegate BrokerProgress; + + //********************************************************************* + // Member: PLUGIN_VERSION + // + /// + /// The broker interface version number, set by Zorro, currently at 2. + /// + //********************************************************************* + private const int PLUGIN_VERSION = 2; + + //********************************************************************* + // Member: isConnected + // + /// + /// Indicates whether the plug-in is connected to the TD Ameritrade + /// server API. + /// + /// + /// + /// True = connected; False = not connected. + /// + //********************************************************************* + private static bool isConnected; + + //********************************************************************* + // Member: verbosityLevel + // + /// + /// The verbosity level of the diagnostic messages from the plug-in + /// + /// + /// + /// Verbosity Levels are: + /// + /// 0 = Few messages + /// 1 = More importanat messages (default) + /// 2 = Even more messages, including command parameters + /// 3 = Still more messages, include skipped trades, possible + /// outliers and + /// unction parameter errors + /// 7 = Extensive diagnostic messages, all messages + /// +512 Print all messages to the Zorro log window as well as the + /// log file. + /// + //********************************************************************* + public static Verbosity verbosityLevel = Verbosity.Extreme | Verbosity.TimeStamp | Verbosity.LineNumbers; + + //********************************************************************* + // Member: TestMode + // + /// + /// Indicates whether the plug-in is in test mode. + /// + /// + /// + /// True = in test mode; False = in live mode. + /// + //********************************************************************* + public static bool TestMode = false; + #endregion PRIVATE MEMBERS + + #region CONSTRUCTOR + //********************************************************************* + // Constructor: TDAmerAPI + // + /// + /// The constructor for this class. + /// + //********************************************************************* + static TDAmerAPI() + { + // Get the current domain + AppDomain currentDomain = AppDomain.CurrentDomain; + + // Set an event handler for when the normal resolution of an + // assembly fails + currentDomain.AssemblyResolve += new ResolveEventHandler(LoadFromSameFolder); + } + #endregion CONSTRUCTOR + + #region PRIVATE METHODS + //********************************************************************* + // Event Handler: LoadFromSameFolder + // + /// + /// Handle firing of the 'AssemblyResolve' event. + /// + /// + /// + /// Object raising this event. + /// + /// + /// + /// Event arguments. + /// + /// + /// + /// This should take place when Zorro is trying to determine which + /// plug-ins to display in its window. + /// + //********************************************************************* + static Assembly + LoadFromSameFolder + ( + object sender, + ResolveEventArgs args + ) + { + // Get the folder path that we are executing in. Should be plug-ins + // directory + string folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + + // Create a name for this assembly + string assemblyPath = Path.Combine(folderPath, new AssemblyName(args.Name).Name + ".dll"); + + // If we are already in the plug-ins folder exit + if (File.Exists(assemblyPath) == false) return null; + + // Not in the plug-ins folder, load this assembly + Assembly assembly = Assembly.LoadFrom(assemblyPath); + + // Return reference to the assembly + return assembly; + } + #endregion PRIVATE METHODS + + #region ZORRO INTERFACE METHODS + //********************************************************************* + // Method: DLLMain + // + /// + /// Method used to define the entry point for this DLL. + /// + /// + /// + /// Name of the module we're running under. + /// + /// + /// + /// May contain reason for call. + /// + /// + /// + /// Not used. + /// + //********************************************************************* + [DllExport("DLLMain", CallingConvention = CallingConvention.StdCall)] + public static void + DLLMain + ( + IntPtr hModule, + UInt32 ul_reason_for_call, + IntPtr lpReserved + ) + { + // Should be left blank. Nothing to do. + } + + //********************************************************************* + // Method: BrokerOpen + // + /// + /// Called at startup for all broker DLLs found in the Plugin folder. + /// Retrieves the name of the broker, and sets up two callback + /// functions. + /// + /// + /// + /// Output, char[32] array to be filled with the name of the broker + /// + /// + /// + /// Input, pointer to a int BrokerError(char* message) function, to be + /// called for printing broker messages (usually error messages) in + /// Zorro's message window. + /// + /// + /// + /// Input, pointer to a int BrokerProgress(int progress=0) function, to + /// be called repeatedly when broker operations take longer than a + /// second. + /// + /// + /// + /// Broker interface version number (PLUGIN_VERSION). + /// + /// + /// + /// NOTE: Using DLLExport library so Zorro will see the approriate + /// functions in this bridge DLL. + /// + //********************************************************************* + [DllExport("BrokerOpen", CallingConvention = CallingConvention.Cdecl)] + public static int + BrokerOpen + ( + StringBuilder name, + BrokerErrorDelegate fpError, + BrokerProgressDelegate fpProgress + ) + { + // Give the name of this plugin back to Zorro + name.Clear(); + name.Append("TD Ameritrade"); + + // Provide a pointer to the broker message function + BrokerError = fpError; + + // Provide a pointer to the progress function for operations that + // take longer than 1.0 secs + BrokerProgress = fpProgress; + + // Return the current plug-in version + return PLUGIN_VERSION; + } + + //********************************************************************* + // Method: BrokerLogin + // + /// + /// Login or logout to the broker's API server; called in [Trade] mode + /// or for downloading historical price data. If the connection to the + /// server was lost, i.e. due to to Internet problems or server weekend + /// maintenance, Zorro calls this function repeatedly in regular inter- + /// vals until it is logged in again. Make sure that the function inter- + /// nally detects the login state and returns safely when the user was + /// still logged in. + /// + /// + /// + /// TDA initial refresh token.. + /// + /// + /// + /// Left blank for TDAmeritrade + /// + /// + /// + /// Input, account type for logging in; either "Real" or "Demo". + /// + /// + /// + /// Optional output, char[1024] array to be filled with all user's + /// account numbers as subsequent zero-terminated strings, ending with + /// "" for the last string. Only the first account number is used by + /// Zorro. + /// + /// + /// + /// Login state: 1 when logged in, 0 otherwise. + /// + //********************************************************************* + [DllExport("BrokerLogin", CallingConvention = CallingConvention.Cdecl)] + public static int + BrokerLogin + ( + string user, + string pwd, + string type, + StringBuilder accounts + ) + { + isConnected = false; + + // Information about this plug-in and disclaimer + BrokerError("TD Ameritrade - Zorro Plug-In"); + BrokerError("Copyright © 2020 by Clyde W. Ford."); + BrokerError("All Rights Reserved."); + BrokerError("\r\nFree for use under LGPL-3.0 license."); + BrokerError("For Non-commercial use only."); + BrokerError("USE AT YOU OWN RISK."); + + // Set the working directory first + Broker.WORKING_DIR = Directory.GetCurrentDirectory(); + + // Set the database connection string + DBLib.Classes.DataAccess.SetConnString(Broker.WORKING_DIR + "/Data/tda.db"); + + // Was SETTINGS initialization successful? + if (Broker.InitSettings(user)) + { + // YES: Log the entrance to this method + LogHelper.Log($"\r\n\r\n{Resx.GetString("BROKER_LOGIN")}..."); + + // Get the operating mode of the plug-in + opMode = type.ToLower() == "demo" ? OpMode.Demo : OpMode.Real; + + LogHelper.Log($"Plug-in is in {opMode} mode."); + + // Create the database tables. If creation not success return + // a failure code to stop the Zorro trading engine + if (!Broker.CreateDbTables()) + { + // Log the error + LogHelper.Log(LogLevel.Critical, $"{Resx.GetString("TABLES_NOT_CREATED")}"); + return 0; + } + + // Call the broker login method + isConnected = Broker.Login(); + + // Add the TD Ameritrade account to the accounts as the one and only + // account + accounts.Append(Broker.settings.TdaAccountNum + "\0" + ""); + + // Log exit from method + LogHelper.Log($"{Resx.GetString("EXITING")} {Resx.GetString("BROKER_LOGIN")}..."); + LogHelper.Log($" {Resx.GetString("RETURN_CODE")} = {isConnected}"); + } + else + { + // NO: Settings file could not be initialized. + LogHelper.Log(LogLevel.Error, "Settings file (tda.json) could not be initialized."); + LogHelper.Log(LogLevel.Error, "This file must be present for the plug-in to operate."); + LogHelper.Log(LogLevel.Error, "Please verify the existence of this file, then try again."); + } + + // Return connected code + return isConnected ? 1 : 0; + } + + //********************************************************************* + // Method: BrokerTime + // + /// + /// Get status of connection and server time. + /// + /// + /// + /// Output containg current server time as UTC (GMT+0) with no daylight + /// savings time. + /// + /// NOTE: pTimeGMT is in OLE date/time format, which is a double. In + /// C# use .ToOADate() to convert from normal system date time format. + /// + /// PTimeGMT is retrieved from the headers sent back with a response + /// from the TD Ameritrade servers. + /// + /// + /// + /// 0, if connection to the server lost. Zorro will call for new login + /// through BrokerLogin. + /// + /// 1, if connection to server is ok, but the market is closed or trade + /// orders are not being accepted at this time. + /// + /// 2, if connection is ok and the market is open for trading at least + /// one of the subscribed assets. + /// + //********************************************************************* + [DllExport("BrokerTime", CallingConvention = CallingConvention.Cdecl)] + public static int + BrokerTime + ( + IntPtr pTimeGMT + ) + { + // Method member + double? serverTime; + + // Log entering this method + LogHelper.Log($"\r\n{Resx.GetString("BROKER_TIME")}..."); + + // Call the implementation method and return the result + int cnx = Broker.Time(out serverTime); + + // Log the time, if not null + if (serverTime != null) + LogHelper.Log(" Server Time: " + DateTime.FromOADate((double)serverTime).ToString("s")); + + // Is the server time present? + if (serverTime != null) + { + // YES: Execute the following in an unsafe code block + unsafe + { + // Cast the IntPtr as a double pointer + Double* dPtr = (Double*)pTimeGMT; + + // If double pointer is not NULL, store server time at + // the address pointed to + if ((int)dPtr > 0) *dPtr = (double)serverTime; + } + } + + // Log exit + switch(cnx) + { + case 0: + // Log connection lost to Zorro window + LogHelper.Log(LogLevel.Warning, $"{Resx.GetString("CONN_LOST")}."); + break; + + case 1: + // Log only to file + LogHelper.Log(LogLevel.Caution, $"{Resx.GetString("CONN_OK_MARKET_CLOSED")}"); + break; + + case 2: + // Log only to file + LogHelper.Log(LogLevel.Info, $"{Resx.GetString("CONN_OK_MARKET_OPEN")}"); + break; + + default: + break; + } + + // Return the connection status + return cnx; + } + + //********************************************************************* + // Method: BrokerAsset + // + /// + /// Subscribes to an asset, or returns information about an asset. + /// + /// + /// + /// INPUT: name of the asset, i.e. "EUR/USD" or "NAS100". TD Ameri- + /// trade accepts "/" in a currency pair. + /// + /// + /// + /// OUTPUT (OPTIONAL): Current ask price of the asset, or NULL for + /// subscribing the asset. An asset must be subscribed before any + /// information about it can be retrieved. + /// + /// + /// + /// OUTPUT (OPTIONAL): Current difference of ask and bid price of the + /// asset. + /// + /// + /// + /// OUTPUT (OPTIONAL): Current trade volume of the asset, or 0 when the + /// volume is unavailable, as for currencies, indexes, or CFDs. + /// + /// + /// + /// + /// + /// + /// OUTPUT (OPTIONAL): Cost of 1 PIP profit or loss per lot, in units of + /// the account currency. If not directly supported, calculate it as + /// decribed under asset list. + /// + /// + /// + /// OUTPUT (OPTIONAL): Minimum order size, i.e. number of contracts for + /// 1 lot of the asset. For currencies it's usually 10000 with mini + /// accounts and 1000 with micro accounts. For CFDs it's usually 1, but + /// can also be a fraction of a contract, e.g. 0.1. + /// + /// + /// + /// OUTPUT (OPTIONAL): Required margin for buying 1 lot of the asset in + /// units of the account currency. Determines the leverage. If not + /// directly supported, calculate it as decribed under asset list. + /// + /// + /// + /// OUTPUT (OPTIONAL): Rollover fee for long trades, i.e. interest that + /// is added to or subtracted from the account for holding positions + /// overnight. The returned value is the daily fee per 10,000 contracts + /// for currencies, and per contract for all other assets, in units of + /// the account currency. + /// + /// + /// + /// OUTPUT (OPTIONAL): Rollover fee for short trades. + /// + /// + /// + /// 1, when the asset is available and the returned data is valid, + /// 0, otherwise. + /// + /// An asset that returns 0 after subscription will not be traded. + /// + /// + /// + /// Pointer and calling conventions to C++ have values defined as an + /// array of doubles. Place desired values into element [0] of the + /// related array. + /// + //********************************************************************* + [DllExport("BrokerAsset", CallingConvention = CallingConvention.Cdecl)] + public static int + BrokerAsset + ( + string asset, + IntPtr pPrice, + IntPtr pSpread, + IntPtr pVolume, + IntPtr pPip, + IntPtr pPipCost, + IntPtr pLotAmount, + IntPtr pMarginCost, + IntPtr pRolloverLong, + IntPtr pRolloverShort + ) + { + // Method member + bool subscribe = false; + + // Log entering this method + LogHelper.Log($"\r\n{Resx.GetString("BROKER_ASSET")}..."); + + // Test for connection to TD Ameritrade API + if (!isConnected) return 0; + + // Call Broker.Asset implementation method to get the asset + TDAsset tdAsset = Broker.Asset(asset); + + // Was asset found and data obtained? + if (tdAsset != null) + { + // Process whether this asset needs to be subscribed to, or not. + // True => asset needs to be subscribed to, False => asset already + // subscribed to + subscribe = Broker.Subscription(tdAsset); + + + //************************************************************* + // + // NOTE: The pointers this method is called with from Zorro are + // not always consistent. Sometimes a pointer is inexplicably 0. + // Therefore, using managed double arrays is not reliable and + // we're kickin' it old-school through unmanaged code to get + // the C++ double pointers, test them for 0, and point them to + // the correct value if they are non-zero. + // + //************************************************************* + unsafe + { + // Subscribing to the asset? + if (!subscribe) + { + // NO: Get a pointer to the Price + Double* dPtr = (Double*)pPrice; + if ((int)dPtr > 0) *dPtr = tdAsset.Price; + + // Spread + dPtr = (Double*)pSpread; + if ((int)dPtr > 0) *dPtr = tdAsset.Spread; + + // Volume + dPtr = (Double*)pVolume; + if ((int)dPtr > 0) *dPtr = tdAsset.Volume; + + // Pip + dPtr = (Double*)pPip; + if ((int)dPtr > 0) *dPtr = tdAsset.Pip; + + // Pip Cost + dPtr = (Double*)pPipCost; + if ((int)dPtr > 0) *dPtr = tdAsset.PipCost; + + // Lot Amount + dPtr = (Double*)pLotAmount; + if ((int)dPtr > 0) *dPtr = tdAsset.LotAmount; + + // Margin Cost + dPtr = (Double*)pMarginCost; + if ((int)dPtr > 0) *dPtr = tdAsset.MarginCost; + + // For TD Ameritrade Rollover options only apply to + // Forex currency pairs, which are currently not + // supported, + if (tdAsset.AssetType == "CURRENCY") + { + // Rollover (Long) + dPtr = (Double*)pRolloverLong; + if ((int)dPtr > 0) *dPtr = tdAsset.forex.RolloverLong; + + // Rollover (Short) + dPtr = (Double*)pRolloverShort; + if ((int)dPtr > 0) *dPtr = tdAsset.forex.RolloverShort; + } + else + { + // Rollover (Long) + dPtr = (Double*)pRolloverLong; + if ((int)dPtr > 0) *dPtr = 0.0; + + // Rollover (Short) + dPtr = (Double*)pRolloverShort; + if ((int)dPtr > 0) *dPtr = 0.0; + } + } + } + + // Log subscription or retrieval for this asset + LogHelper.Log(subscribe ? + $" {Resx.GetString("SUCCESS")}: {Resx.GetString("SUBSCRIBING_TO")} {tdAsset.Symbol}" : + $" {Resx.GetString("SUCCESS")}: {Resx.GetString("RETRIEVING_INFO_FOR")} {tdAsset.Symbol}"); + + // Return that this asset has valid data, and can be traded + return 1; + } + + // PAST HERE: Asset not found, and will not be traded. + LogHelper.Log(LogLevel.Error, $" {asset} {Resx.GetString("NOT_FOUND")}."); + return 0; + } + + + //********************************************************************* + // Method: BrokerHistory2 (optional) + // + /// + /// Returns the price history of an asset. + /// + /// + /// + /// INPUT: THe name of the asset, e.g. "EUR/USD" or "MSFT." + /// + /// + /// + /// INPUT: UTC start date/time of the price history in OLE format. This + /// has only the meaning of a seek-no-further date; the relevant date for + /// the begin of the history is tEnd. + /// + /// + /// + /// INPUT: UTC END date/time of the price history. If the price history + /// is not available in UTC time, but in the brokers's local time, the + /// plugin must convert it to UTC. + /// + /// + /// + /// INPUT: The time period of a tick in minutes. Usual values are 0 for + /// single price ticks (T1 data; optional), 1 for one-minute (M1) + /// historical data, or a larger value for more quickly filling the + /// LookBack period before starting a strategy. + /// + /// + /// + /// INPUT: The maximum number of ticks to be filled; guaranteed to be + /// 300 or less. + /// + /// + /// + /// OUTPUT: A pointer to ann array of up to 300 TICK structs (defined in + /// include\trading.h) filled in with the ask price history. The ticks + /// array is filled in reverse order from tEnd or until either the tick + /// time reaches tStart or the num of ticks reaches nTicks, whichever + /// happens first. The most recent tick, closest to tEnd, is at the + /// start of the array. In the case of T1 data, or when only a single + /// price is available, all prices in the TICK struct can be set to the + /// same value. + /// + /// + /// + /// (1) Number of ticks returned; OR, + /// + /// (2) 0 when no ticks could be returned, + /// + /// e.g. when the server was offline, the asset was not subscribed, or + /// price history was not available for the given date/time. + /// + //********************************************************************* + [DllExport("BrokerHistory2", CallingConvention = CallingConvention.Cdecl)] + public static int + BrokerHistory2 + ( + string asset, + double tStart, + double tEnd, + int nTickMinutes, + int nTicks, + IntPtr data + ) + { + // Entry to method indicator + LogHelper.Log($"\r\n{Resx.GetString("BROKER_HISTORY2")} ({asset})..."); + + // Reasons for returning a zero code + if (!isConnected || String.IsNullOrEmpty(asset) || nTicks == 0) return 0; + + // Call implementation function to get an array of Tick structures + Tick[] outTicks = Broker.History(asset, tStart, tEnd, nTickMinutes, nTicks); + + // Do we have any ticks? + if (outTicks.Length == 0) + { + // NO: Log no ticks returned + LogHelper.Log(LogLevel.Warning, $" {Resx.GetString("NO_TICKS_RETURNED")} {asset}."); + + // Return failure code + return 0; + } + + //***************************************************************** + // + // NOTE: The layout of managed and unmanaged arrays isn't the same. + // Zorro passes a pointer to a T6 array, and we need to use an + // unmanaged pointer to get the original C++ T6 array pointer. We + // will loda a T6 array of structures at that address. + // + //***************************************************************** + unsafe + { + // Zorro passes us a pointer to a an array of T6 structs. We + // have to make that an IntPtr in C#, but create a new pointer + // and cast it as a Tick pointer, so it will advance by the + // size of one tick structure through adding '1'. + Tick* ptr = (Tick*)data; + + // If output pointer is NOT NULL store historical data to it + if ((int)ptr != 0) + { + // Iterate through the array of ticks returned from the Browser + // History implementation and layout a sequential T6 structure + // array beginning at the address pointed to by the IntPtr this + // method is called with. + for (int i = 0; i < outTicks.Length; ++i) + { + // Advance pointer by size of a Tick structure + *(ptr + i) = + + // Store a new Tick structure at this location + new Tick( + outTicks[i].time, + outTicks[i].fOpen, + outTicks[i].fClose, + outTicks[i].fHigh, + outTicks[i].fLow, + outTicks[i].fVal, + outTicks[i].fVol + ); + } + } + } + + // Log # ticks returned + LogHelper.Log($" {Resx.GetString("TICKS_RETURNED")} {asset} ({outTicks.Length})."); + + // Return the count of the number of ticks + return outTicks.Length; + } + + //********************************************************************* + // Method: BrokerAccount + // + /// + /// Returns the current account status. + /// + /// + /// + /// Input, new account number or NULL for using the current account. + /// + /// + /// + /// Optional output, current balance on the account. + /// + /// + /// + /// Optional output, current value of all open trades; the difference + /// between account equity and balance. If not available, it can be + /// replaced by a Zorro estimate with the SET_PATCH broker command. + /// + /// + /// + /// Optional output, current total margin bound by all open trades. + /// + /// + /// + /// 1 when the account is available and the returned data is valid, + /// 0 when a wrong account was given or the account was not found. + /// + /// + /// + /// This version of the plug-in DOES NOT SUPPORT multiple TD Ameritrade + /// accounts. + /// + //********************************************************************* + [DllExport("BrokerAccount", CallingConvention = CallingConvention.Cdecl)] + public static int + BrokerAccount + ( + string account, + IntPtr pdBalance, + IntPtr pdTradeVal, + IntPtr pdMarginVal + ) + { + // Gate-keeping test + if (!isConnected) return 0; + + // Log entry to this method + LogHelper.Log($"\r\n{Resx.GetString("BROKER_ACCOUNT")}..."); + + // If account is NULL, use the current TD Ameritrade account + if (string.IsNullOrEmpty(account)) account = Broker.settings.TdaAccountNum; + + // Call back-end implementation + AccountBalance acctBalance = Broker.Account(account); + + // If account balance object is NULL, return invalid acct code + if (acctBalance == null) + { + // Log account not found + LogHelper.Log(LogLevel.Error, $" {Resx.GetString("ACCOUNT_NUM")} {account} {Resx.GetString("NOT_FOUND")}."); + + // Return with failure + return 0; + } + + // Past here, we have a valid account balance object, give Zorro + // the information desired + unsafe + { + // NO: Get a pointer to the Account Balance + Double* dPtr = (Double*)pdBalance; + if ((int)dPtr > 0) *dPtr = acctBalance.Balance; + + // Trade Value + dPtr = (Double*)pdTradeVal; + if ((int)dPtr > 0) *dPtr = acctBalance.TradeValue; + + // Margin Value + dPtr = (Double*)pdMarginVal; + if ((int)dPtr > 0) *dPtr = acctBalance.MarginValue; + } + + // Log account found + LogHelper.Log(LogLevel.Error, $" {Resx.GetString("ACCOUNT_NUM")} {account} {Resx.GetString("FOUND")}."); + + // Return valid data code + return 1; + } + + //********************************************************************* + // Method: BrokerBuy2 + // + /// + /// Enters a long or short trade at market or limit. Also used for NFA + /// compliant accounts to close a trade by opening a position in the + /// opposite direction. + /// + /// + /// + /// INPUT: name of the asset, i.e. "EUR/USD" or "MSFT". + /// + /// + /// + /// INPUT: Number of contracts, positive for a long trade and negative + /// for a short trade. The number of contracts is the number of lots + /// multiplied with the LotAmount. If LotAmount is < 1 (e.g. for a CFD + /// with 0.1 contracts lost size), the number of lots is given here + /// instead of the number of contracts. + /// + /// + /// + /// INPUT: 'Safety net' stop loss distance to the opening price, or 0 + /// for no stop. This is not the real stop loss, which is handled by + /// the trade engine. Placing the stop is not mandatory. NFA compliant + /// orders do not support a stop loss; in that case dStopDist is 0 for + /// opening a trade and -1 for closing a trade by opening a position in + /// the opposite direction. + /// + /// NOTE: This plug-in does not support FOREX, FUTURE, or FUTURE OPTION + /// trading, so it should not see a trade with a stop distance of -1. + /// Also, as of 5/15/2020, STOP and STOP LIMIT orders could no be + /// placed via the plug-in. TD Ameritrade has been notified and may + /// have a resolution. + /// + /// + /// + /// INPUT (optional): Ask/bid price for limit orders, set up by the + /// OrderLimit Zorro function, or 0 for market orders. + /// + /// NOTE: This plug-in does support LIMIT orders but not STOP LIMIT + /// orders, see above comment under dStopDist. + /// + /// + /// + /// OUTPUT (optional): The current asset price at which the trade was + /// opened. + /// + /// + /// + /// OUTPUT (optional): The fill amount, needed for order types other + /// than FOK (fill or kill) orders. + /// + /// + /// + /// Trade or order ID number = when either the order was filled, or a + /// GTC order was successfully placed. If the broker does not support + /// individual trades or ID numbers, a unique 6-digit number, f.i. from + /// a counter, can serve as a trade ID. + /// + /// 0 = when a FOK or IOC order was not filled within 30 seconds + /// (adjustable with the SET_WAIT command). Unfilled FOK / IOC orders + /// must be cancelled. + /// + /// -1 = when the trade identifier is a UUID that must be retrieved + /// with the GET_UUID command. + /// + /// -2 = when the broker API did not respond at all within 30 seconds. + /// The order must then be cancelled. Zorro displays a "possible orphan" + /// warning. + /// + /// -3 = when the trade was entered, but got no ID yet. The ID is then + /// taken over from the next BrokerBuy2 call that returns a ID > 0. + /// + /// + /// + /// NOTE: NFA compliance refers to the role that the National Futures + /// Association (NFO) plays in regulating the futures market and is + /// usually meant to refer to rule 2-43b, implemented in 2009 by NFA, + /// which states that "forex deal members (FDM) and retail foreign + /// exchange dealers (RFED) cannot allow clients to hedage and must + /// offset posititons on a first-in-first-out (FIFO) basis. + /// + /// The effect of this rule is: + /// + /// (1) Banning of price adjustments to executed customer orders, + /// except to resolve a complaint that is in the customer's favor. + /// + /// (2) Increasesed transparency for customers and brings forex trading + /// practices in line with those of the equities and futures markets. + /// + /// NFA is a self-regulatory body of the FOREX industry, and their + /// compliance rules apply ONLY TO trading of currency exchange trades. + /// + //********************************************************************* + [DllExport("BrokerBuy2", CallingConvention = CallingConvention.Cdecl)] + public static int + BrokerBuy2 + ( + string asset, + int nAmount, + double dStopDist, + double dLimit, + IntPtr pPrice, + IntPtr pFill + ) + { + // Method members + string orderType = (nAmount > 0) ? Resx.GetString("BUYING") : Resx.GetString("SELLING"); + string marketType = (dLimit > 0) ? Resx.GetString("LIMIT") + " " + Resx.GetString("PRICE_OF") + " " + dLimit.ToString("N4") : Resx.GetString("MARKET"); + + // Log entry to this method + LogHelper.Log($"\r\n{Resx.GetString("BROKER_BUY2")}..."); + + // Gate-keeping function for connection + if (!isConnected) return 0; + + // If stop distance is -1, then trying to close an NFA trade but + // this plug-in does not support NFA trades, so return a code as + // if trade was canceled. + if (dStopDist == -1) return 0; + + // Log the trade + LogHelper.Log($" {Resx.GetString("TRADE")}: {orderType} {Math.Abs(nAmount)} {Resx.GetString("SHARES_OF")} {asset} {Resx.GetString("AT")} {marketType}."); + + // Call back-end implementation and get a Trade object in return + Trade trade = Broker.Buy(asset, nAmount, dStopDist, dLimit); + + // Is the Zorro Trade Id > 0? + if (trade.ZorroTradeId > 0) + { + // YES: Good trade. Do we have a price also? + if (trade.Price > 0.00) + { + unsafe + { + // YES: We have a price. Define a double pointer to the + // output parameter passed to us by Zorro + Double* dPtr = (Double*)pPrice; + + // If this passed parameter is NOT NULL, store price + // data to it + if ((int)dPtr != 0) *dPtr = trade.Price; + } + } + + // Log a success + LogHelper.Log($" {Resx.GetString("SUCCESS")}"); + + // Return the Zorro Trade Id, since the TD Trade Id will be + // a long value .gt. what Zorro can handle + return trade.ZorroTradeId; + } + else + { + // Is a combo OPTION trade in process? + if (Broker.ComboLegs > 0) + { + // YES: Waiting for more trade orders to make up a + // combination order + return 0; + } + else + { + // Log a failure + LogHelper.Log(LogLevel.Warning, $" {Resx.GetString("FAILURE")}"); + + // NO: Is TD Trade Id .lte. zero? + if (trade.TDTradeId <= 0) + { + // YES: Return it as an int + return (int)trade.TDTradeId; + } + else + { + // NO: Strange return. Should never get here but if + // we do, then return 0, for no trade + return 0; + } + } + } + } + + //********************************************************************* + // Method: BrokerStop + // + /// + /// Optional method to adjust the stop loss limit of an open trade if + /// it had an original stop (dStopDist != 0 in BrokerBuy2) and if it's + /// not an NFA account. If this function is not provided, the original + /// stop loss is never updated. Only for non-NFA compliant accounts. + /// + /// + /// + /// INPUT: The trade ID number as returned by BrokerBuy2 + /// + /// + /// + /// INPUT: The new stop loss price. Must be by a sufficient distance + /// (broker dependent) below the current price for a long trade, and + /// above the current price for a short trade. + /// + /// + /// + /// 0, if no open trade with this ID found, otherwise nonzero. + /// + /// + /// + /// NOTE: This method is not implemented by the plug-in becasue STOP + /// and STOP LIMIT orders are not implented. See BrokerSell2 for more + /// information on STOP LOSS order implementation + /// + //********************************************************************* + /* + [DllExport("BrokerStop", CallingConvention = CallingConvention.Cdecl)] + public static int + BrokerStop + ( + int nTradeID, + double dStop + ) + { + // Log entry to this method + BrokerError("BrokerStop..."); + + // Gate-keeper to make sure a connection to TD Ameritrade exists + if (!connected) return 0; + } + */ + + //********************************************************************* + // Method: BrokerSell2 + // + /// + /// Closes a trade either completely or partially, at MARKET; only for + /// non-NFA compliant accounts. Partial closing is seen primarily in + /// FOREX trading, and currently it appears that TD Ameritrade does not + /// suuport partial closing as it is NFA compliant. + /// + /// + /// + /// INPUT: The Zorro trade ID as returned by BrokerBuy2. + /// + /// + /// + /// INPUT: The number of contracts or lots to be closed, positive for a + /// long trade and negative for a short trade (see BrokerBuy2). If less + /// than the original size of the trade, the trade is partially closed. + /// NOTE: This plug-in WILL NOT partially close a trade. It's all or + /// nothing. + /// + /// + /// + /// INPUT (optional): The ask/bid price for a LIMIT order, or 0, if + /// closing at the market. + /// + /// + /// + /// OUTPUT (optional): The close price of the trade. + /// + /// + /// + /// OUTPUT (optional): The total rollover (swap) fee of the trade. + /// + /// + /// + /// OUTPUT (optional): The total profit/loss of the trade in account + /// currency units. + /// + /// + /// + /// OUTPUT (optional): The fill amount of the trade. + /// + /// + /// + /// (1) Trade ID number of the remaining 'reduced' trade when it was + /// partially closed; OR, + /// + /// (2) THe original trade ID number when it was completely closed; OR; + /// + /// (3) 0 when the trade could not be closed. + /// + /// + /// + /// This Zorro method is being implemented by the plug-in to provide + /// information about the closing, cost, and profit of a trade. Anytime + /// a trade is closed with a call to this method, the trade will be + /// completely closed because TD Ameritrade does not support partially + /// closed trades. + /// + //********************************************************************* + [DllExport("BrokerSell2", CallingConvention = CallingConvention.Cdecl)] + public static int + BrokerSell2 + ( + int nTradeID, + int nAmount, + double dLimit, + IntPtr pClose, + IntPtr pCost, + IntPtr pProfit, + IntPtr pFilled + ) + { + // Log entry to this method + LogHelper.Log($"\r\n{Resx.GetString("BROKER_SELL")}"); + + // Gate-keeping for this method + if (!isConnected) return 0; + + // Get a trade object from attempting to close the trade + Trade trade = Broker.Sell(nTradeID, nAmount, dLimit); + + // If the trade to close was made, fill-in the OUTPUT variables + if (trade.StatusCode > 0) { + + // SUCCESS: Manipulate pointers and store double values in an + // unmanaged code block + unsafe + { + // Define a double pointer to the closing price + Double* dPtr = (Double*)pClose; + if ((int) dPtr != 0) *dPtr = trade.Close; + + // Cost + dPtr = (Double*)pCost; + if ((int)dPtr != 0) *dPtr = trade.Cost; + + // Profit + dPtr = (Double*)pProfit; + if ((int)dPtr != 0) *dPtr = trade.Profit; + + // Filled + dPtr = (Double*)pFilled; + if ((int)dPtr != 0) *dPtr = trade.Filled; + } + + LogHelper.Log($"{Resx.GetString("SUCCESS")}: {Math.Abs(nAmount)} shares of {trade.Asset}."); + } + else + { + // FAILURE + LogHelper.Log(LogLevel.Error, $"{Resx.GetString("FAILURE")}: {Math.Abs(nAmount)} shares of {trade.Asset}."); + } + + // Return the status code + return trade.StatusCode; + } + + //********************************************************************* + // Method: BrokerTrade + // + /// + /// Returns the status of an open or recently closed trade. + /// + /// + /// + /// INPUT: The Zorro trade ID number as returned by BrokerBuy2. + /// + /// + /// + /// OUTPUT (optional): The enter price of the asset including spread. Not + /// available for NFA compliant accounts. + /// + /// + /// + /// OUTPUT (optional): The current price of the asset including spread. + /// + /// + /// + /// OUTPUT (optional): The total rollover fee (swap fee) of the trade so + /// far. Not available for NFA compliant accounts. + /// + /// + /// + /// OUTPUT (optional): The profit or loss of the trade so far. Not + /// available for NFA compliant accounts. Possible wrong values due to + /// API bugs can be replaced by Zorro estimates with the SET_PATCH + /// broker command + /// + /// + /// + /// (1) Number of contracts (amount) of the given trade ID number + /// + /// (2) 0 when no trade with this ID could be found + /// + /// (3) A negative number, if the trade was recently closed. + /// + /// NOTE: IF the returned value is nonzero, the output pointers must be + /// filled. + /// + //********************************************************************* + [DllExport("BrokerTrade", CallingConvention = CallingConvention.Cdecl)] + public static int + BrokerTrade + ( + int nTradeID, + IntPtr pOpen, + IntPtr pClose, + IntPtr pCost, + IntPtr pProfit + ) + { + // Log entry to this method + LogHelper.Log($"\r\n{Resx.GetString("BROKER_TRADE")}..."); + + // Gate-keeper for this method + if (!isConnected) return 0; + + // Call the back-end implementation + Trade trade = Broker.BrokerTrade(nTradeID); + + // Is the status code non-zero? + if (trade.StatusCode != 0) + { + // YES: Fill-in the pointers + unsafe + { + // Define a double pointer to the closing price + Double* dPtr = (Double*)pClose; + if ((int)dPtr != 0) *dPtr = 0.0; + + // Open + dPtr = (Double*)pOpen; + if ((int)dPtr != 0) *dPtr = trade.Price; + + // Cost + dPtr = (Double*)pCost; + if ((int)dPtr != 0) *dPtr = 0.0; + + // Profit + dPtr = (Double*)pProfit; + if ((int)dPtr != 0) *dPtr = 0.0; + } + } + + // Log status of this trade + LogHelper.Log($" {Resx.GetString("TRADE")}: {trade.Asset} {trade.Status.ToUpper()}"); + + // Return to Zorro with the trade status code + return trade.StatusCode; + } + + //********************************************************************* + // Method: BrokerCommand + // + /// + /// Optional function. Sets various plugin parameters or returns asset + /// specific extra data. This function is not mandatory, as it is not + /// used by Zorro's trade engine; but it can be called in scripts + /// through the brokerCommand function for special purposes. + /// + /// + /// + /// INPUT: The command from the brokerCommand list. + /// + /// + /// + /// INPUT: The parameter or data to the command. + /// + /// + /// + /// 0 when the command is not supported by this broker plugin, otherwise + /// the data to be retrieved. + /// + /// + /// + /// + //********************************************************************* + [DllExport("BrokerCommand", CallingConvention = CallingConvention.Cdecl)] + public static double + BrokerCommand + ( + int nCommand, + IntPtr dwParameter + ) + { + // Log the command + // Uncomment the following two lines to debug Zorro's brokerCommand + string param = ((int)dwParameter) > 1 ? "\"" + Marshal.PtrToStringAnsi(dwParameter) + "\"" : dwParameter.ToString(); + LogHelper.Log($"brokerCommand({(ZorroCommand)nCommand}, {param})"); + + // Gate-keeper for this method + if (!isConnected) return 0; + + // Switch to see if this plug-in is handlinging any of the commands + switch((ZorroCommand)nCommand) + { + case ZorroCommand.SET_SYMBOL: + // Call the SET_SYMBOL command implementation + return Broker.SetSymbol(nCommand, Marshal.PtrToStringAnsi(dwParameter)); + + case ZorroCommand.GET_POSITION: + // Call the backend command to get the position on the asset, + // after converting from a pointer to a string + return Broker.GetPosition(nCommand, Marshal.PtrToStringAnsi(dwParameter)); + + case ZorroCommand.GET_OPTIONS: + // Get the option chain of the underlying asset + return Broker.GetOptions(nCommand, dwParameter); + + case ZorroCommand.GET_UNDERLYING: + return Broker.GetUnderlying(nCommand, (int)dwParameter); + + case ZorroCommand.SET_COMBO_LEGS: + return Broker.SetComboLegs(nCommand, (int)dwParameter); + + case ZorroCommand.SHOW_RESOURCE_STRING: + return Broker.ShowResourceString(nCommand, Marshal.PtrToStringAnsi(dwParameter)); + + case ZorroCommand.REVIEW_LICENSE: + return Broker.ReviewLicense(nCommand, 0); + + case ZorroCommand.GET_TEST_ASSETS: + // Get the asset list + return Broker.GetTestAssets(nCommand, dwParameter); + + case ZorroCommand.GET_ASSET_LIST: + // Get the asset list + return Broker.GetAssetList(nCommand, dwParameter); + + case ZorroCommand.SET_SELL_SELL_SHORT: + // Set what to do if selling more shares than owned + return Broker.SetSellSellShort(nCommand, (int)dwParameter); + + case ZorroCommand.SET_VERBOSITY: + // Set the verbosity level + verbosityLevel = (Verbosity)(int)(dwParameter); + if (verbosityLevel > Verbosity.Intermediate) + BrokerError($"Plug-in verbosity level now set to {verbosityLevel.ToString().ToUpper()}"); + return 1; + + case ZorroCommand.SET_TESTMODE: + TestMode = (int)dwParameter == 1; + return 1; + /* + case ZorroCommand.DO_EXERCISE: + // Exercise sthe give number of contracts of an option + return Broker.DoExercise(nCommand, Marshal.PtrToStringAnsi(dwParameter)); + */ + + case ZorroCommand.GET_COMPLIANCE: + // Full NFA compliance + return 15; + + default: + break; + } + + // Command is not handled by this plug-in + return 0; + } + #endregion ZORRO INTERFACE METHODS + } +} diff --git a/TDAmeritradeZorro/TDAmeritradeZorro.csproj b/TDAmeritradeZorro/TDAmeritradeZorro.csproj new file mode 100644 index 0000000..3b0e5e1 --- /dev/null +++ b/TDAmeritradeZorro/TDAmeritradeZorro.csproj @@ -0,0 +1,238 @@ + + + + + Debug + AnyCPU + {5E3CB65B-FEAE-48C4-B16A-B25729626408} + Library + Properties + TDAmeritradeZorro + TDAmeritrade + v4.7.2 + 512 + + + + + + true + full + false + ..\Debug\ + TRACE;DEBUG + prompt + 4 + false + true + + + pdbonly + true + ..\Release\ + TRACE + prompt + 4 + x86 + + + true + bin\x86\Debug\ + TRACE;DEBUG + full + x86 + prompt + MinimumRecommendedRules.ruleset + false + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + + + + Lib\DBLib.dll + + + $(SolutionDir)packages\DllExport.1.7.1\gcache\$(DllExportMetaXBase)\$(DllExportNamespace)\$(DllExportMetaLibName) + False + False + + + + + + + + + + + + + + + + + + + + + + + + + + Form + + + LicenseForm.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Form + + + AuthForm.cs + + + + + + + + + + LicenseForm.cs + + + Designer + + + Designer + + + Designer + + + Designer + + + + + + 1.7.1 + + + + + + + + + + ECHO F | xcopy "$(TargetPath)" "$(SolutionDir)TDAmeritradeZorro\Lib\TDAmeritrade.dll" /Y +ECHO F | xcopy "$(SolutionDir)TDAmeritradeZorro\Lib\TDAmeritrade.dll" "c:\Users\cwford\Zorro\Plugin\TDAmeritrade.dll" /Y +ECHO F | xcopy "$(SolutionDir)TDAmeritradeZorro\Lib\DBLib.dll" "c:\Users\cwford\Zorro\Plugin\DBLib.dll" /Y +ECHO F | xcopy "$(SolutionDir)TDAmeritradeZorro\Lib\Microsoft.Data.Sqlite.dll" "c:\Users\cwford\Zorro\Plugin\Microsoft.Data.Sqlite.dll" /Y +ECHO F | xcopy "$(SolutionDir)TDAmeritradeZorro\Lib\sqlite3.dll" "c:\Users\cwford\Zorro\Plugin\sqlite3.dll" /Y + + + + 038BE701-017D-4617-AB55-6DBABBD67084 + DllExport.dll + TDAmer + true + x86 + 1 + false + false + false + false + 30000 + 2 + 0 + 0 + 0 + + + + + + + + + + + + + \ No newline at end of file diff --git a/TDAmeritradeZorro/Utilities/ApiHelper.cs b/TDAmeritradeZorro/Utilities/ApiHelper.cs new file mode 100644 index 0000000..75ac44a --- /dev/null +++ b/TDAmeritradeZorro/Utilities/ApiHelper.cs @@ -0,0 +1,230 @@ +//***************************************************************************** +// File: ApiHelper.cs +// +// Author: Clyde W. Ford +// +// Date: April 29, 2020 +// +// Description: Helper class for TD Ameritrade API requests. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using TDAmeritradeZorro.Authentication; + +namespace TDAmeritradeZorro.Utilities +{ + //************************************************************************* + // Class: ApiHelper + // + /// + /// A helper class for the Web API functions, which builds the JSON or + /// url-encoded payload for API requests. + /// + //************************************************************************* + public static class ApiHelper + { + //********************************************************************* + // Method: JsonRawData + // + /// + /// Create a Web API data object for raw data sent in the body of the + /// request. + /// + /// + /// + /// Json data. + /// + /// + /// + /// Web API data object with Json data as the raw data parameter. + /// + //********************************************************************* + public static object[] + JsonRawData + ( + string Data, + string AccountId, + bool UseAccessToken + ) + { + // Return a data object for the Web API call + return new object[] + { + // Header data + null, + + // www-form-urlencoded-data + null, + + // Raw data + Data, + + // Get an access token, if needed + UseAccessToken ? AuthToken.GetAuthToken().AccessToken : null, + + // Account id + AccountId, + + // Order id + null + }; + } + + //********************************************************************* + // Method: JsonUrlEncodedData + // + /// + /// Create a Web API data object for raw data sent in the header of the + /// request. + /// + /// + /// + /// Json data. + /// + /// + /// + /// Web API data object with Json data as the url-encoded data + /// parameter. + /// + //********************************************************************* + public static object[] + UrlEncodedData + ( + string Data, + bool UseAccessToken + ) + { + // Return a data object for the Web API call + return new object[] + { + // Header data + null, + + // www-form-urlencoded-data + Data, + + // Raw data + null, + + // Get an access token, if needed + UseAccessToken ? AuthToken.GetAuthToken().AccessToken : null, + + // Account id + null, + + // Order id + null + }; + } + + //********************************************************************* + // Method: AccountData + // + /// + /// Create a Web API data object for id data sent in the querystring of + /// the request. + /// + /// + /// + /// Id data. + /// + /// + /// + /// Web API data object with string data as the Id data parameter. + /// + //********************************************************************* + public static object[] + AccountDataWithQueryString + ( + string Data, + string QueryString, + bool UseAccessToken + ) + { + // Return a data object for the Web API call + return new object[] + { + // Header data + QueryString, + + // www-form-urlencoded-data + null, + + // Raw data + null, + + // Get an access token, if needed + UseAccessToken ? AuthToken.GetAuthToken().AccessToken : null, + + // Account id + Data, + + // Order id + null + }; + } + + //********************************************************************* + // Method: AccountOrderData + // + /// + /// Create a Web API data object for id data sent in the querystring of + /// the request. + /// + /// + /// + /// Id data. + /// + /// + /// + /// Web API data object with string data as the Id data parameter. + /// + //********************************************************************* + public static object[] + AccountOrderData + ( + string AccountData, + string OrderData, + bool UseAccessToken + ) + { + // Return a data object for the Web API call + return new object[] + { + // Header data + null, + + // www-form-urlencoded-data + null, + + // Raw data + null, + + // Get an access token, if needed + UseAccessToken ? AuthToken.GetAuthToken().AccessToken : null, + + // Account id + AccountData, + + // Order id + OrderData + }; + } + } +} diff --git a/TDAmeritradeZorro/Utilities/Helper.cs b/TDAmeritradeZorro/Utilities/Helper.cs new file mode 100644 index 0000000..4f32000 --- /dev/null +++ b/TDAmeritradeZorro/Utilities/Helper.cs @@ -0,0 +1,299 @@ +//***************************************************************************** +// File: Helper.cs +// +// Author: Clyde W. Ford +// +// Date: April 29, 2020 +// +// Description: Helper methods static class. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Net; +using System.Runtime.InteropServices; +using TDAmeritradeZorro.Classes; +using DBLib.Classes; +using TDAmeritradeZorro.Classes.TDA; + +namespace TDAmeritradeZorro.Utilities +{ + //************************************************************************* + // Class: Helper + // + /// + /// A static class used for helper functions. + /// + //************************************************************************* + public static class Helper + { + // The separator characters used for breaking apart the usernam/refresh + // token pair and the password/access token pair + private static readonly string SEP = ";#|."; + + //********************************************************************* + // Members: iconLicenseStr, iconAuthStr + // + /// + /// The base-64 icon strings for the Windows Forms license and authori- + /// zation forms. + /// + //********************************************************************* + private static string iconLicenseStr = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAQXSURBVFhH7ZZdiJRVGMefsx+uhuu6i/mBYLtuwyKGrOwaJYEXETMUBbUDiheVF5lIiohIQR+UVNtFgSmkFSFZwdJKZqUzIIgXIYJ2tyUqautikR/rarW6tvv6+78fw+bM7OuM6I3+4TfPOe975jzPOed5zzl218uls6kObEVQLVmD3cnMjyrQTxtmtsqhPM+zf53ZcXN2mrraBm9GSQEMYauDask6Q6czVaCfTzEvqVxA8rEPNhNVdkcqe81/isodeZzk8G8Y8Wtm4yAJu5xze9KZ5BT/KVIAdXDfjRDp61hfntl6TF4baIZ8ed4S/nQ/pYn89wHsMjgErIg9bs7tJohJlK2KKRxU4UZ1ZJK5aUJDxdoV0VB3KnMlLPfCNpboS+wnsBwWEMQr2Pdu1xLkiQFoOdZBn//AbBVB1d2xACSCuIz5PqjZdL6Sx2IDmHzhqj258/dZrGU7zIeHoDF87Yt6TeLIQO2sk5ctceSiPdt1oolnc6E2bDJae0LLKtijSoqCIgfWk7EfvL/6gDUfHfCcJz85rsIliLK8erjSNZBiVXpbMeKN0H6Y5/3wMk52+q0Q0/4w5gBo8D/EzsAIYY6SKvrPBJgGM2Aq1HkVzoahctgznKuN9hZ9CWqXE9N+CvNfULOa2AA+eqPVup5PfE7x6QJoNC1iw7vt3/bMa6CYk2bna9jm10LtSGX+wmh2pJbYAM5PGW/dS5t7GPruAhyGkzTrW/5xT/38Q+eCP5mdhRcE77VcxeRiA4gTS16J6ZzZ9492OuttnGjfLEusxvFXEOVITulsUtMU+T12ywGgNKwBd+LBSdb5Tpt9t3i2PrfC8lwTv9HZM3hLATD6JzDboaq3sdbr3NBmZ6cpP8eQ8xMz8vtr2QHgXEevTkCN5szGV+f91N9Qo1dx8pcq1MGyAsC5TreNoA1pAJ7rbar9AzumOrIpHWDPBDU7D/vKnYG18BQQi71Gsh3Uw7HEBqQd5S2K0aVlK1tzf8kB4FE3nzeB/vxPUSdcUaUzqXqca8/QFqxjXfqN4/5DFUoKAOf6hLpAmaZr1krIl3PbcdoHlwhTS7MLorX/hY6S3IouqHLTAeBcI94EuoToxrOCBzrrC6kedFXTYaTM1M6nC8mL9LOQu4KC96VOC4rDqJXD6BGV+dPPrFc7RW3JCvoz/qiLRU6MdhFmTlDL6Qp7/1EW/xT2T7bhvI3ppkQALdAP9GPHIXenu+3C2TjYHzq/BkqoOyccvh06F1vgf3lDndPXqkGB1oxC9dgcK5oDkjrB6PjUzZmq7QVdRHQAqXNZJVkCxof1yKkutcdgDU4O+09KFR4bQdMezUA56KAqqjFnQKID3elbg5o/C8rkyAqNVGe+Pklty3o3GfS56kr2BU70/J4KyOw6pTBm76maw9QAAAAASUVORK5CYII="; + private static string iconAuthStr = "/9j/4AAQSkZJRgABAQEAYABgAAD/4QBoRXhpZgAATU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAAExAAIAAAARAAAATgAAAAAAAXbyAAAD6AABdvIAAAPocGFpbnQubmV0IDQuMi4xMAAA/9sAQwACAQEBAQECAQEBAgICAgIEAwICAgIFBAQDBAYFBgYGBQYGBgcJCAYHCQcGBggLCAkKCgoKCgYICwwLCgwJCgoK/9sAQwECAgICAgIFAwMFCgcGBwoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoK/8AAEQgAGAAYAwEhAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A8s+AP/BK79o/9pjwzoniL4UeLvhzdzeILZp9P0Sfx5ax6jtXcWDW2fMVgEZiMcKM9K2tU/4I8ftLaLqE2k6r8U/g7b3VtM0Nzb3HxU0+OSGRThkZWYFWBBBBGQRzX4TT4YzCth41lOnyy2vNLonb111XQ/mGjwfmdbCxxEalPlls3UiuidvVJq63R5p+1T+wZ+0z+xtDpOqfG3wRbw6Rr650TxBpOpw31henbu2rNCxAbbyFbBIBIyBmivHx2CxGX4mVCurSXzWqumn1TR4WYZfisrxcsNiFaSt1ummrpprdNHq3/BCdc/8ABUn4aNn7setH/wAo97X0b/wWb07S7f8A4J//AA5v7fT4Uubj4zeKjNcLCoeT/iZan1YDJ/GvrsrS/wBUcQ33n/7hPt8oUf8AUfFX7z/9wHK+Obi7v/8Ag3A8Jt43ZpHt/iUy+F2uiSwQXl0MR57AG5Ax2yKK8fiD4sLff2NO/wBz/Sx4PE13PCN7+wpX+52/Cx8Tfsp/tL+Ov2QPjvov7Qnw203TbvWtBW5Fnb6vC8lu3n20lu+5UdGOElYjDDnHXpXv0/8AwWU+LXiD4dWPwu+Jf7Mvwg8ZaTpusX2p2UPirwzcXfk3F1czXEjKGucD5p3UYGduASeSZy3iDEZbhXh1ThODbbUk3e/L5r+RW+ZGVcSYrKsHLCxpQnBttqabvzct+q/kVjzL9rf/AIKFfHb9sLw14f8Ah54z07w74d8IeF/m0Hwd4N0gWOnWr7SvmBNzEsFLKMthQzYA3Nkrz8wx9bMsU69WybskkrJJKySXZI8zM8yxGbYx4iskm7JJKySSskl0SR//2Q=="; + + //********************************************************************* + // Method: SeparateCreds + // + /// + /// Separate username/refresh and password/accesss token pairs. + /// + /// + /// + /// The input string eneterd into the username or password field + /// + /// + /// + /// A string array with the credential is the first element of the + /// array and the token in the second element. + /// + //********************************************************************* + public static string[] + SeparateCreds + ( + string inputValue + ) + { + // Method members + string SEP_CHAR = string.Empty; + + // Iterate through the separator characters + for (int i = 0; i < SEP.Length; ++i) + { + // Form the separator character + SEP_CHAR = SEP.Substring(i, 1); + + // Does separator character appear twice in the input value? + if (inputValue.Contains(SEP_CHAR + SEP_CHAR)) + { + // YES: Use it to break-up the input value and return the + // separated values. The credential is the first element, + // the token is the second element. + string[] STR_SEP = new string[] { SEP_CHAR + SEP_CHAR }; + return inputValue.Split(STR_SEP, StringSplitOptions.None); + } + } + + // Return a one-element string array with the given input value + return new string[] { inputValue }; + } + + //********************************************************************* + // Method: GetWebPage + // + /// + /// Get a web page from an HTML server. + /// + /// + /// + /// The URL of the page to be retrieved. + /// + /// + /// + /// The web page as a string of HTML. + /// + //********************************************************************* + public static string + GetWebPage + ( + string url + ) + { + // Method member + string result; + + // Create a new web client + var myClient = new WebClient(); + + // Add the user-agent header so the request can go through + myClient.Headers.Add("User-Agent: Other"); + + // Get the response stream from opening and reading the URL + using (Stream response = myClient.OpenRead(url)) + { + // Create a stream reader to read the response + StreamReader sr = new StreamReader(response); + + // Read the response + result = sr.ReadToEnd(); + + // Close the respones stream + response.Close(); + } + + // Return the result of reading the response stream served as a + // result of the original request + return result; + } + + //********************************************************************* + // Method ZorroToTDAssetType + // + /// + /// Convert a Zorro asset type to a TD Ameritrade asset type + /// + /// + /// + /// The Zorro asset type. + /// + /// + /// + /// Formatted JSON string. + /// + //********************************************************************* + public static string + ZorroToTDAssetType + ( + string ZorroAssetType + ) + { + // Do conversion via a switch statement + switch(ZorroAssetType) + { + case "STK": + case "ETF": + return "EQUITY"; + + case "FUND": + return "MUTUAL_FUND"; + + case "OPT": + return "OPTION"; + + default: + return ""; + } + } + + //********************************************************************* + // Method FormatJson + // + /// + /// Formats a JSON string and returns beautified JSON. + /// + /// + /// + /// The JSON string to format. + /// + /// + /// + /// How much to indent each subordinate property group. + /// + /// + /// + /// Formatted JSON string. + /// + //********************************************************************* + public static string + FormatJson + ( + string json, + string indent = " " + ) + { + var indentation = 0; + var quoteCount = 0; + var escapeCount = 0; + json = json.Replace("\r\n", ""); + + var result = + from ch in json ?? string.Empty + let escaped = (ch == '\\' ? escapeCount++ : escapeCount > 0 ? escapeCount-- : escapeCount) > 0 + let quotes = ch == '"' && !escaped ? quoteCount++ : quoteCount + let unquoted = quotes % 2 == 0 + let colon = ch == ':' && unquoted ? ": " : null + let nospace = char.IsWhiteSpace(ch) && unquoted ? string.Empty : null + let lineBreak = ch == ',' && unquoted ? ch + Environment.NewLine + string.Concat(Enumerable.Repeat(indent, indentation)) : null + let openChar = (ch == '{' || ch == '[') && unquoted ? ch + Environment.NewLine + string.Concat(Enumerable.Repeat(indent, ++indentation)) : ch.ToString() + let closeChar = (ch == '}' || ch == ']') && unquoted ? Environment.NewLine + string.Concat(Enumerable.Repeat(indent, --indentation)) + ch : ch.ToString() + select colon ?? nospace ?? lineBreak ?? ( + openChar.Length > 1 ? openChar : closeChar + ); + + return string.Concat(result); + } + + //********************************************************************* + // Method: GetWindowsFormIcon + // + /// + /// Convert the base-64 string of the TD Ameritrade image into an icon + /// for the Windows Form. + /// + //********************************************************************* + public static Icon + GetWindowsFormIcon + ( + FormType formType + ) + { + // Get the correct icon base-64 string + string iconStr = formType == FormType.License ? iconLicenseStr : iconAuthStr; + + // Convert icon string to byte array + byte[] ba = Convert.FromBase64String(iconStr); + + // Convert from byte array to bit map + Bitmap bm = (Bitmap)((new ImageConverter()).ConvertFrom(ba)); + + // Convert bitmap into an icon handle + IntPtr iHandle = bm.GetHicon(); + + // Return the icon + return Icon.FromHandle(iHandle); + } + + //********************************************************************* + // Method: DestroyIcon + // + /// + /// Win32 method to destroy the icon used by the Window Form. + /// + /// + /// + /// Handle of the icon to destroy. + /// + /// + /// + /// True if successful, false if not. + /// + //********************************************************************* + [DllImport("user32.dll", CharSet = CharSet.Auto)] + public extern static bool DestroyIcon + ( + IntPtr handle + ); + + + } +} diff --git a/TDAmeritradeZorro/Utilities/Resx.cs b/TDAmeritradeZorro/Utilities/Resx.cs new file mode 100644 index 0000000..d59ea5e --- /dev/null +++ b/TDAmeritradeZorro/Utilities/Resx.cs @@ -0,0 +1,284 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Resources; +using System.Threading; +using System.Windows.Forms; +using TDAmeritradeZorro.Classes; + +namespace TDAmeritradeZorro.Utilities +{ + //************************************************************************* + // Class: ResxHelper + // + /// + /// A class that supports the use of resource files and globalization. + /// + /// + /// + /// Currently English (U.S.) is the only language supported by this plug-in. + /// + //************************************************************************* + public static class Resx + { + private static Dictionary LangDict = new Dictionary + { + {"Afrikaans", "af"}, + {"Albanian", "sq"}, + {"Arabic (Algeria)", "ar-DZ"}, + {"Arabic (Bahrain)", "ar-BH"}, + {"Arabic (Egypt)", "ar-EG"}, + {"Arabic (Iraq)", "ar-IQ"}, + {"Arabic (Jordan)", "ar-JO"}, + {"Arabic (Kuwait)", "ar-KW"}, + {"Arabic (Lebanon)", "ar-LB"}, + {"Arabic (Libya)", "ar-LY"}, + {"Arabic (Morocco)", "ar-MA"}, + {"Arabic (Oman)", "ar-OM"}, + {"Arabic (Qatar)", "ar-QA"}, + {"Arabic (Saudi Arabia)", "ar-SA"}, + {"Arabic (Syria)", "ar-SY"}, + {"Arabic (Tunisia)", "ar-TN"}, + {"Arabic (U.A.E.)", "ar-AE"}, + {"Arabic (Yemen)", "ar-YE"}, + {"Basque", "eu"}, + {"Belarusian", "be"}, + {"Bulgarian", "bg"}, + {"Catalan", "ca"}, + {"Chinese (Hong Kong)", "zh-HK"}, + {"Chinese (PRC)", "zh-CN"}, + {"Chinese (Singapore)", "zh-SG"}, + {"Chinese (Taiwan)", "zh-TW"}, + {"Croatian", "hr"}, + {"Czech", "cs"}, + {"Danish", "da"}, + {"Dutch (Belgium)", "nl-BE"}, + {"Dutch (Standard)", "nl"}, + {"English", "en"}, + {"English (Australia)", "en-AU"}, + {"English (Belize)", "en-BZ"}, + {"English (Canada)", "en-CA"}, + {"English (Ireland)", "en-IE"}, + {"English (Jamaica)", "en-JM"}, + {"English (New Zealand)", "en-NZ"}, + {"English (South Africa)", "en-ZA"}, + {"English (Trinidad)", "en-TT"}, + {"English (United Kingdom)", "en-GB"}, + {"English (United States)", "en-US"}, + {"Estonian", "et"}, + {"Faeroese", "fo"}, + {"Farsi", "fa"}, + {"Finnish", "fi"}, + {"French (Belgium)", "fr-BE"}, + {"French (Canada)", "fr-CA"}, + {"French (Luxembourg)", "fr-LU"}, + {"French (Standard)", "fr"}, + {"French (Switzerland)", "fr-CH"}, + {"Gaelic (Scotland)", "gd"}, + {"German (Austria)", "de-AT"}, + {"German (Liechtenstein)", "de-LI"}, + {"German (Luxembourg)", "de-LU"}, + {"German (Standard)", "de"}, + {"German (Switzerland)", "de-CH"}, + {"Greek", "el"}, + {"Hebrew", "he"}, + {"Hindi", "hi"}, + {"Hungarian", "hu"}, + {"Icelandic", "is"}, + {"Indonesian", "id"}, + {"Irish", "ga"}, + {"Italian (Standard)", "it"}, + {"Italian (Switzerland)", "it-CH"}, + {"Japanese", "ja"}, + {"Korean", "ko"}, + {"Korean (Johab)", "ko"}, + {"Kurdish", "ku"}, + {"Latvian", "lv"}, + {"Lithuanian", "lt"}, + {"Macedonian (FYROM)", "mk"}, + {"Malayalam", "ml"}, + {"Malaysian", "ms"}, + {"Maltese", "mt"}, + {"Norwegian", "no"}, + {"Norwegian (Bokmål)", "nb"}, + {"Norwegian (Nynorsk)", "nn"}, + {"Polish", "pl"}, + {"Portuguese (Brazil)", "pt-BR"}, + {"Portuguese (Portugal)", "pt"}, + {"Punjabi", "pa"}, + {"Rhaeto-Romanic", "rm"}, + {"Romanian", "ro"}, + {"Romanian (Republic of Moldova)", "ro-MD"}, + {"Russian", "ru"}, + {"Russian (Republic of Moldova)", "ru-MD"}, + {"Serbian", "sr"}, + {"Slovak", "sk"}, + {"Slovenian", "sl"}, + {"Sorbian", "sb"}, + {"Spanish (Argentina)", "es-AR"}, + {"Spanish (Bolivia)", "es-BO"}, + {"Spanish (Chile)", "es-CL"}, + {"Spanish (Colombia)", "es-CO"}, + {"Spanish (Costa Rica)", "es-CR"}, + {"Spanish (Dominican Republic)", "es-DO"}, + {"Spanish (Ecuador)", "es-EC"}, + {"Spanish (El Salvador)", "es-SV"}, + {"Spanish (Guatemala)", "es-GT"}, + {"Spanish (Honduras)", "es-HN"}, + {"Spanish (Mexico)", "es-MX"}, + {"Spanish (Nicaragua)", "es-NI"}, + {"Spanish (Panama)", "es-PA"}, + {"Spanish (Paraguay)", "es-PY"}, + {"Spanish (Peru)", "es-PE"}, + {"Spanish (Puerto Rico)", "es-PR"}, + {"Spanish (Spain)", "es"}, + {"Spanish (Uruguay)", "es-UY"}, + {"Spanish (Venezuela)", "es-VE"}, + {"Swedish", "sv"}, + {"Swedish (Finland)", "sv-FI"}, + {"Thai", "th"}, + {"Tsonga", "ts"}, + {"Tswana", "tn"}, + {"Turkish", "tr"}, + {"Ukrainian", "uk"}, + {"Urdu", "ur"}, + {"Venda", "ve"}, + {"Vietnamese", "vi"}, + {"Welsh", "cy"}, + {"Xhosa", "xh"}, + {"Yiddish", "ji"}, + {"Zulu", "zu"} + }; + + //********************************************************************* + // Member: rm + // + /// + /// The resource manager used to access the current resource file. + /// + //********************************************************************* + private static ResourceManager rm; + + //********************************************************************* + // Member: GetString + // + /// + /// Get the string resource associated with a given key. + /// + /// + /// + /// The key for which a string resource is being sought. + /// + //********************************************************************* + public static string + GetString + ( + string key + ) + { + // Initialize the resource manager if necessary + if (rm == null) + rm = new ResourceManager($"TDAmeritradeZorro.Resources.{Broker.settings.LangResx}", typeof(Resx).Assembly); + + // Get the string resource + string text = rm.GetString(key); + + // If resource found, return it; if not, return an empty string + return text == null ? "" : text.Replace("\\r\\n", Environment.NewLine); + } + + //********************************************************************* + // Member: GetStringFromLang + // + /// + /// Get the string resource associated with a given key. + /// + /// + /// + /// The key for which a string resource is being sought. + /// + /// + /// + /// The language to use. + /// + //********************************************************************* + public static string + GetStringFromLang + ( + string key, + string lang + ) + { + // Initialize the resource manager if necessary + if (rm == null) + rm = new ResourceManager($"TDAmeritradeZorro.Resources.{lang}", typeof(Resx).Assembly); + + // Get the string resource + string text = rm.GetString(key); + + // If resource found, return it; if not, return an empty string + return text == null ? "" : text.Replace("\\r\\n", Environment.NewLine); + } + + //********************************************************************* + // Method: ValidateLang + // + /// + /// Validate a language specification. + /// + /// + /// + /// Language specification entered as xx-YY where xx is the language, + /// and YY is the country or region. -YY is optional. + /// + /// + /// + /// 1 = Language specification is valid + /// -1 = Language specification is invalid + /// -2 = Language specification is valid, but language not on-file + /// 0 = Unspecified error + /// + //********************************************************************* + public static int + ValidateLang + ( + string lang + ) + { + // Method member + string resxString; + string[] resxNames; + + try + { + // Is the language specification valid? + if (LangDict.ContainsValue(lang)) + { + // YES: Form resource string + resxString = $"TDAmeritradeZorro.Resources.{lang}.resources"; + + // Get all the resoure names in this assembly + resxNames = Assembly.GetExecutingAssembly().GetManifestResourceNames(); + + // Is the language spec in the resources names? + foreach (string resxName in resxNames) + if (resxName == resxString) return 1; + + // Language spec not in assembly + return -2; + } + else + { + // NO: Return invalid language code + return -1; + } + } + catch(Exception) + { + // ERROR: Return unspecified error code + return 0; + } + } + } +} diff --git a/TDAmeritradeZorro/WebApi/AuthForm.Designer.cs b/TDAmeritradeZorro/WebApi/AuthForm.Designer.cs new file mode 100644 index 0000000..2a9cf95 --- /dev/null +++ b/TDAmeritradeZorro/WebApi/AuthForm.Designer.cs @@ -0,0 +1,132 @@ +using System; +using System.Drawing; +using TDAmeritradeZorro.Classes; +using TDAmeritradeZorro.Utilities; + +namespace TDAmeritradeZorro.WebApi +{ + //************************************************************************* + // Class: AuthForm + // + /// + /// This is the designer class behind the Windows Form that shows a web + /// browser control for interacting with the TD Ameritrade HTML servers. + /// + //************************************************************************* + partial class AuthForm + { + //********************************************************************* + // Member: IContainer + // + /// + /// Required designer variable. + /// + //********************************************************************* + private System.ComponentModel.IContainer components = null; + + //********************************************************************* + // Member: webBrowser + // + /// + /// The web browser control that occupies this Windows Form + /// + //********************************************************************* + public System.Windows.Forms.WebBrowser webBrowser; + + //********************************************************************* + // Member: icon + // + /// + /// The icon for the Web Browser Form window. + /// + //********************************************************************* + public Icon icon; + + //********************************************************************* + // Method: Dispose + // + /// + /// Clean up any resources being used. + /// + /// + /// + /// True if managed resources should be disposed; otherwise, false. + /// + //********************************************************************* + protected override void + Dispose + ( + bool disposing + ) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + //********************************************************************* + // Method: InitializeComponent + // + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + //********************************************************************* + private void InitializeComponent() + { + try + { + // Enable the web browser to support IE Edge emulation, + // including the use of modern Javascript listeners + EnsureBrowserEmulationEnabled(); + + // Create the icon for the Windows Form + icon = Helper.GetWindowsFormIcon(TDAmeritradeZorro.Classes.FormType.Auth); + + // Width and height of the form and th web browser control + int width = 475; + int height = 425; + + // Initialize the web browser control + this.webBrowser = new System.Windows.Forms.WebBrowser(); + + // Suspend layout while initializing form and browser + this.SuspendLayout(); + + // + // webBrowser + // + this.webBrowser.Dock = System.Windows.Forms.DockStyle.Fill; + this.webBrowser.Location = new System.Drawing.Point(0, 0); + this.webBrowser.MinimumSize = new System.Drawing.Size(20, 20); + this.webBrowser.Name = "webBrowser"; + this.webBrowser.Size = new System.Drawing.Size(width, height); + this.webBrowser.TabIndex = 0; + this.webBrowser.ScrollBarsEnabled = false; + + // + // AuthForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(width, height); + this.Controls.Add(this.webBrowser); + this.Name = "TD Ameritrade"; + this.Text = "TD Ameritrade"; + this.Icon = icon; + + // Resume laying out form + this.ResumeLayout(false); + } + catch (Exception e) + { + // Log thet error + LogHelper.Log(LogLevel.Error, e.Message); + } + } + #endregion + } +} \ No newline at end of file diff --git a/TDAmeritradeZorro/WebApi/AuthForm.cs b/TDAmeritradeZorro/WebApi/AuthForm.cs new file mode 100644 index 0000000..59e46e1 --- /dev/null +++ b/TDAmeritradeZorro/WebApi/AuthForm.cs @@ -0,0 +1,226 @@ +//***************************************************************************** +// File: AuthForm.cs +// +// Author: Clyde W. Ford +// +// Date: May 1, 2020 +// +// Description: The Authorization Form (Windows Forms) used to interact with +// TD Ameritrade to get the firs access token. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using Microsoft.Win32; +using System; +using System.Collections.Specialized; +using System.Web; +using System.Windows.Forms; +using TDAmeritradeZorro.Classes; +using TDAmeritradeZorro.Utilities; + +namespace TDAmeritradeZorro.WebApi +{ + //************************************************************************* + // Class: AuthForm + // + /// + /// Create a Windows From that holds a Web Browser control that can be used + /// to interact with the TD Ameritrade HTML servers to interactively allow + /// the user to get the first access token. + /// + //************************************************************************* + public partial class AuthForm : Form + { + #region CLASS MEMBERS + //********************************************************************* + // Member: redirectUri + // + /// + /// Holds the URL that TD Ameritrade will redirect to with the authen- + /// tication code is the query string of address on the address bar. + /// + /// + /// + /// The plug-in uses a redirect URL of http://127.0.0.1. + /// + //********************************************************************* + private String redirectUri; + + //********************************************************************* + // Member: query + // + /// + /// Holds the query string returned when an authenticate code is found. + /// + /// + /// + /// Query string is held as a name-value pair collection. + /// + //********************************************************************* + private NameValueCollection query; + public NameValueCollection Query + { + get { return query; } + } + #endregion CLASS MEMBERS + + #region CONSTRUCTOR + //********************************************************************* + // Constructor: AuthForm + // + /// + /// The constructor used to create the Windows Form that holds the + /// browser control for interacting with the TD Ameritrade HTML servers. + /// + /// + /// + /// The URL that causes TD Ameritrade to display an authentification + /// login screen. + /// + /// + /// + /// Redirect URL used when sending back a page that has a '404 Error' + /// but whose address bar contaain an authentication code. + /// + //********************************************************************* + public AuthForm + ( + string loginLinkUri, + string redirectUri + ) + { + // Normal component initializatin + InitializeComponent(); + + // Center the Form + this.CenterToScreen(); + + // Save the redirecty URL + this.redirectUri = redirectUri; + + // Create a web browser control that opens the login page from TD + // Ameritrade + this.webBrowser.Url = new Uri(loginLinkUri); + + // Set an event handler to handle the web browser navigating to any + // new page + this.webBrowser.Navigated += WebBrowser_Navigated; + } + #endregion CONSTRUCTOR + + #region PRIVATE METHODS + //********************************************************************* + // Method: WebBrowser_Navigated + // + /// + /// Event handler invoked whenever the web browser control navigates to + /// a new page + /// + /// + /// + /// The web browser control giving rise to this event. + /// + /// + /// + /// The event arguments + /// + //********************************************************************* + private void + WebBrowser_Navigated + ( + object sender, + WebBrowserNavigatedEventArgs e + ) + { + // Get the query string as a name/value pair collection + NameValueCollection query = HttpUtility.ParseQueryString(e.Url.Query); + + // Is this the browser navigating to the '404 Error' page, where + // the authentication code is in the URL? + if (e.Url.AbsoluteUri.StartsWith(this.redirectUri) && query["code"] != null) + { + // YES: Save the query + this.query = query; + + // Set a success dialog result + this.DialogResult = DialogResult.OK; + + // Close this web browser dialog + this.Close(); + } + } + + + //********************************************************************* + // Method: EnsureBrowserEmulationEnabled + // + /// + /// Modify the Windows Registry so that browser emulation supports + /// modern browsers like IE Edge. + /// + /// + /// + /// The name of the .exe file, which this method is registered with. + /// + /// + /// + /// TRUE to install the modifications in the registry, FALSE to unin- + /// stall them. + /// + //********************************************************************* + private void + EnsureBrowserEmulationEnabled + ( + string exename = "Zorro.exe", + bool uninstall = false + ) + { + try + { + // Get the registry subkey for browser emulation + using ( + var rk = Registry.CurrentUser.OpenSubKey( + @"SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION", true) + //@"SOFTWARE\WOW6432Node\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION", true) + ) + { + // Are we installing expanded emulation? + if (!uninstall) + { + // YES: Get the current value + dynamic value = rk.GetValue(exename); + + // Change the current value of the key, if needed + if (value == null) + rk.SetValue(exename, (uint)11001, RegistryValueKind.DWord); + } + else + // NO: Uninstall the key's value in the registery + rk.DeleteValue(exename); + } + } + catch (Exception e) + { + // Log the error in the log file only + LogHelper.Log(LogLevel.Error, $" {Resx.GetString("ERROR")}: {Resx.GetString("ENABLING_BROWSER_EMULATION")}. " + e.Message); + } + } + #endregion PRIVATE METHODS + } +} \ No newline at end of file diff --git a/TDAmeritradeZorro/WebApi/Classes/QueryStringHelper.cs b/TDAmeritradeZorro/WebApi/Classes/QueryStringHelper.cs new file mode 100644 index 0000000..f41b7e4 --- /dev/null +++ b/TDAmeritradeZorro/WebApi/Classes/QueryStringHelper.cs @@ -0,0 +1,463 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Collections.Specialized; +using System.Web; + +namespace TDAmeritradeZorro.WebApiLib.Classes +{ + /// + /// Used to parse and manipulate an HTTP querystring [GET request] + /// + public class QueryStringHelper + { + /// + /// Gets the current query string collection + /// + public NameValueCollection QueryStringCollection { get; private set; } + + /// + /// Initializes a new instance of the class using the passed querystring + /// + /// The querystring to use + public QueryStringHelper(string qs) + { + this.QueryStringCollection = HttpUtility.ParseQueryString(qs); + } + + /// + /// Initializes a new instance of the class using the querystring in the current HttpContext.Request + /// + public QueryStringHelper() + { + if (HttpContext.Current == null || HttpContext.Current.Request == null) + throw new NullReferenceException("There is no HttpContext or the Request is null"); + + this.QueryStringCollection = new NameValueCollection(HttpContext.Current.Request.QueryString); + } + + /// + /// Initializes a new instance of the class from an HttpRequest + /// + /// The HTTP request to use + public QueryStringHelper(HttpRequest request) + { + this.QueryStringCollection = new NameValueCollection(request.QueryString); + } + + /// + /// Initializes a new instance of the class from a NameValueCollection + /// + /// The name value collection + public QueryStringHelper(NameValueCollection nvCollection) + { + this.QueryStringCollection = new NameValueCollection(nvCollection); + } + + /// + /// Checks whether a key with the name exists + /// + /// The name to check + /// True if it exists or false if it doesn't + public bool NameExists(string name) + { + return !String.IsNullOrEmpty(GetValueByName(name)); + } + + /// + /// Gets a querystring value by name + /// + /// The name of the key to get + /// The string value associated with the key + public string GetValueByName(string name) + { + return QueryStringCollection.Get(name); + } + + /// + /// Gets a string value by name and if it is null or empty returns a default value instead + /// + /// The value + /// The default value to return when null or empty + /// A value + public string GetValueByName(string name, string defaultValue) + { + string value = QueryStringCollection.Get(name); + + if (!String.IsNullOrEmpty(value)) + return value; + else + return defaultValue; + } + + /// + /// Gets a querystring value and converts it to the destination type + /// + /// The type to convert it to + /// The name of the key to get from the query string + /// The value of the key converted to a type of T + /// Throws a System.FormatException if the type cannot be converted + public T GetValueByName(string name) where T : struct + { + string value = GetValueByName(name); + + if (String.IsNullOrEmpty(value)) + { + return default(T); + } + + return (T)Convert.ChangeType(value, typeof(T)); + } + + /// + /// Gets a querystring value and converts it to the destination type using the converter + /// + /// The type to convert the value to + /// The name of the key to get from the query string + /// The delegate to perform the conversion + /// Whether to throw an exception when there is an error converting (default = false) + /// The value of the key converted to a type of T + /// var cats = qs.GetMultipleValuesByName<int>("cat", x => Convert.ToInt32(x)) + public T GetValueByName(string name, Func converter, bool throwOnError = false) + { + if (converter == null) + throw new ArgumentNullException("converter", "The delegate converter cannot be null"); + + string value = GetValueByName(name); + + if (String.IsNullOrEmpty(value) && throwOnError == false) + { + return default(T); + } + + try + { + return converter(value); + } + catch (Exception) + { + if (throwOnError) + throw; + else + return default(T); + } + } + + /// + /// Gets a querystring value and converts it to the destination type. If it cannot be converted then the defaultValue is returned instead. + /// + /// The type to convert it to + /// The name of the key to get + /// The default value to return if it cannot be converted + /// The value of the key converted to a type of T or the default value + public T GetValueByName(string name, T defaultValue) where T : struct + { + if (String.IsNullOrEmpty(GetValueByName(name))) + { + return defaultValue; + } + + try + { + return GetValueByName(name); + } + catch (FormatException) + { + return defaultValue; + } + } + + /// + /// Gets the named querystring value multiple times as a string collection + /// + /// The name + /// An enumerable (which can be empty if there are no matches) + public IEnumerable GetMultipleValuesByName(string name) + { + var vals = QueryStringCollection.GetValues(name); + + if (vals == null) + { + return Enumerable.Empty(); + } + else + { + return vals; + } + } + + /// + /// Gets multiple query string values for the given name (eg. ?cat=123&cat=434&cat=454) + /// + /// The Type you want to convert the value to + /// The name of the query string + /// An enumerable of type T. If a value cannot be converted it is not returned. + public IEnumerable GetMultipleValuesByName(string name) where T : struct + { + var vals = QueryStringCollection.GetValues(name); + + if (vals == null) + { + return Enumerable.Empty(); + } + else + { + var list = new List(); + + foreach (var value in vals) + { + if (!String.IsNullOrEmpty(value)) + { + T converted; + + try + { + converted = (T)Convert.ChangeType(value, typeof(T)); + list.Add(converted); + } + catch (FormatException) + { + // do nothing + } + } + } + + return list; + } + } + + /// + /// Gets multiple query string values for the given name (eg. ?cat=123&cat=434&cat=454) + /// + /// The Type you want to convert the value to + /// The name of the query string + /// A default value that is returned if it cannot be converted + /// An enumerable of type T. If a value cannot be converted then default value is returned instead. + public IEnumerable GetMultipleValuesByName(string name, T defaultValue) where T : struct + { + var vals = QueryStringCollection.GetValues(name); + + if (vals == null) + { + return Enumerable.Empty(); + } + else + { + var list = new List(); + + foreach (var value in vals) + { + if (!String.IsNullOrEmpty(value)) + { + T converted; + + try + { + converted = (T)Convert.ChangeType(value, typeof(T)); + } + catch (FormatException) + { + converted = defaultValue; + } + + list.Add(converted); + } + } + + return list; + } + } + + /// + /// Gets multiple query string values for the given name (eg. ?cat=123&cat=434&cat=454) + /// + /// The Type you want to convert the value to + /// The name of the query string + /// A delegate to perform the conversion + /// Whether to throw an exception when an error occurs during conversion + /// An enumerable of type T + public IEnumerable GetMultipleValuesByName(string name, Func converter, bool throwOnError = false) + { + if (converter == null) + throw new ArgumentNullException("converter", "The delegate converter cannot be null"); + + var vals = QueryStringCollection.GetValues(name); + + if (vals == null) + { + return Enumerable.Empty(); + } + else + { + var list = new List(); + + foreach (var value in vals) + { + try + { + list.Add(converter(value)); + } + catch (Exception) + { + if (throwOnError) + throw; + } + } + + return list; + } + } + + /// + /// Adds the specified name and value to the querystring. If the value already exists it is added with a comma. + /// + /// The name to add + /// The value to add + public void Add(string name, string value) + { + QueryStringCollection.Add(name, value); + } + + /// + /// Adds the specified name and value (converted to a string) to the querystring. If the value already exists it is added with a comma. + /// + /// The name to add + /// The value to add + public void Add(string name, object value) + { + this.Add(name, value.ToString()); + } + + /// + /// Adds a new value or replaces it if a key with that value already exists + /// + /// The name to add + /// The value to add + public void AddOrReplace(string name, string value) + { + QueryStringCollection.Set(name, value); + } + + + /// + /// Adds a new value or replaces it if a key with that value already exists. The value is converted to a string. + /// + /// The name to add + /// The value to add + public void AddOrReplace(string name, object value) + { + AddOrReplace(name, value.ToString()); + } + + /// + /// Removes the key with the name + /// + /// The name of the key to remove + public void RemoveByName(string name) + { + QueryStringCollection.Remove(name); + } + + /// + /// Removes all the keys that have a specific value + /// + /// The value to check for + /// A count of the keys removed + public int RemoveByValue(string value) + { + int removed = 0; + + for (int i = QueryStringCollection.Keys.Count - 1; i > 0; i--) + { + if (QueryStringCollection.Get(i) == value) + { + QueryStringCollection.Remove(QueryStringCollection.Keys[i]); + removed++; + } + } + + return removed; + } + + /// + /// Clears this querystring + /// + public void Clear() + { + QueryStringCollection.Clear(); + } + + /// + /// Returns a count of the keys in the querystring + /// + /// A positive integer or 0 if no values + public int Count() + { + return QueryStringCollection.Count; + } + + /// + /// Returns a that represents the current querystring + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return GetQueryString(); + } + + /// + /// Gets the querystring as a string of name and value pairs delimited with an ampersand + /// + /// The querystring as a string + public string GetQueryString() + { + return GetQueryString(false); + } + + /// + /// Gets the querystring as a string of name and value pairs delimited with an ampersand + /// + /// If set to true keys with no value are removed + /// The querystring as a string with empty keys removed + public string GetQueryString(bool removeEmpty) + { + StringBuilder qs = new StringBuilder(); + + int len = QueryStringCollection.Keys.Count; + + for (int i = 0; i < len; i++) + { + string val = QueryStringCollection.Get(i); + if (String.IsNullOrEmpty(val) && removeEmpty) + continue; + + string key = HttpUtility.UrlEncode(QueryStringCollection.GetKey(i)); + var items = QueryStringCollection.Get(i).Split(','); // required as Get returns CSV for values with same key + + foreach (var value in items) + { + qs.AppendFormat("{0}={1}&", key, HttpUtility.UrlEncode(value)); + } + } + + if (len > 0) + { + qs.Length -= 1; + } + + return qs.ToString(); + } + + /// + /// Converts the querystring NameValueCollection to a generic Dictionary that can be easily interated Over + /// + /// A Dictionary of string name and value pairs + public Dictionary ToDictionary() + { + return this.QueryStringCollection.Cast().Select(s => new { Key = s, Value = this.QueryStringCollection[s] }).ToDictionary(p => p.Key, p => p.Value); + } + } +} diff --git a/TDAmeritradeZorro/WebApi/Classes/TDAmeritradeREST.cs b/TDAmeritradeZorro/WebApi/Classes/TDAmeritradeREST.cs new file mode 100644 index 0000000..fe2f0d1 --- /dev/null +++ b/TDAmeritradeZorro/WebApi/Classes/TDAmeritradeREST.cs @@ -0,0 +1,230 @@ +//***************************************************************************** +// File: TDAmeritradeREST.cs +// +// Author: Clyde W. Ford +// +// Date: April 29, 2020 +// +// Description: Submit HTTP requests to the TD Ameritrade system through its +// API, and received responses. +// +// Copright (c) 2020 Clyde W. Ford. All rights reserved. +// +// License: LGPL-3.0 (Non-commercial use only) +// +// DISCLAIMER: +// +// This Zorro plug-in is offered on an AS IS basis with no claims or warranties +// that it is fit or complete for any given purpose. YOU USE THIS PLUG-IN AT +// YOUR OWN RISK. +// +// Since the plug-in may be used as part of a system to trade financial instru- +// ments, the user of this plug-in accepts complete and total responsibility +// for any damages, monetary or otherwise, that arize from the use of the plug- +// in, and holds harmless the author of the plug-in for any damages, financial +// or otherwise, incurred. +// +// For further information, see the Disclaimer included with this plug-in. +//***************************************************************************** +using System; +using System.IO; +using System.Linq; +using System.Net; +using TDAmeritradeZorro.Classes; +using TDAmeritradeZorro.Utilities; + +namespace TDAmeritradeZorro.WebApi.Classes +{ + //************************************************************************ + // Class: TDAmeritradeREST + // + /// + /// Class that instantiates a query method to access all of the TD + /// Ameritrade REST API methods. + /// + //************************************************************************ + public class TDAmeritradeREST + { + #region CLASS MEMBERS + //********************************************************************* + // Member: BaseAddress + // + /// + /// The base server address for the Web API + /// + //********************************************************************* + private const string BaseAddress = "https://api.tdameritrade.com/v1"; + #endregion CLASS MEMBERS + + #region CLASS CONSTRUCTOR + //********************************************************************* + // Constructor: TDAmeritradeREST + // + /// + /// Constructor for the TD Ameritrade REST web API. + /// + //********************************************************************* + public TDAmeritradeREST() { } + #endregion CLASS CONSTRUCTOR + + #region PUBLIC METHODS + //********************************************************************* + // Method: QueryApi + // + /// + /// Access a TD Ameritrade Web API method + /// + /// + /// + /// Enum for method being called. + /// + /// + /// + /// Calling parameters. + /// + /// + /// + /// The response from the TD Ameritrade API as a string. + /// + //********************************************************************* + public string + QueryApi + ( + ApiMethod apiMethod, + object[] Params + ) + { + // The content returned to the caller + string resultContent = null; + + // The URI called on the TD Ameritrade REST API + string reqUri = string.Empty; + + try + { + // Get URI = base + method URI. + reqUri = BaseAddress + apiMethod.GetAttribute("Name"); + + // Are we adding a query string to the URI? + if (Params[0] != null) + + // YES: Add the query string after adding a "?" + reqUri += "?" + Params[0].ToString(); + + // Add account id (param 4), if necessary + if (Params[4] != null) + reqUri = reqUri.Replace("{account_id}", Params[4].ToString()); + + // Add order id (param 5), if necessary + if (Params[5] != null) + reqUri = reqUri.Replace("{order_id}", Params[5].ToString()); + + // Create a new http web request object with the URI + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(reqUri); + + // Set the METHOD for the request in 'Prompt' attribute + request.Method = apiMethod.GetAttribute("Prompt"); + + // Add the content type of this query, in 'GroupName' attribute + request.ContentType = apiMethod.GetAttribute("GroupName"); + + // Add the content length header, if raw data is being sent + if (Params[2] != null && Params[2].ToString().Length > 0) + { + request.ContentLength = Params[2].ToString().Length; + } + + // Request accepts JSON data, which is returned from TDA + request.Accept = "application/json"; + + // Add the authorization header, if included + if (Params[3] != null) + request.Headers.Add("Authorization: Bearer " + Params[3].ToString()); + + // Set the request time out at 10 minutes (in miliseconds) + request.Timeout = 10 * 60 * 1000; + + // Need to send url-encoded form content or raw JSON content in + // the request BODY? + if (Params[1] != null || Params[2] != null) + + // YES: Send it by writing to the request stream + using (StreamWriter sw = new StreamWriter(request.GetRequestStream())) + { + // If sending url-encoded data, write it to the stream + if (Params[1] != null) + sw.Write(Params[1].ToString()); + else + // If sending raw JSON data, write it to the stream + if (Params[2] != null) + sw.Write(Params[2].ToString()); + + // Close the stream writer + sw.Close(); + } + + // Create and retrieve the web response + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + // Was a 200 status code returned? + if (response.StatusCode == HttpStatusCode.OK || + response.StatusCode == HttpStatusCode.Created || + response.StatusCode == HttpStatusCode.Accepted) + { + // YES: Create a response stream + using (Stream stream = response.GetResponseStream()) + { + // Create a stream reader to read the response stream + using (StreamReader sr = new StreamReader(stream)) + { + // Read the response stream + resultContent = sr.ReadToEnd(); + + // Close the stream reader + sr.Close(); + } + } + + // Is this a request for market hours? + if (apiMethod == ApiMethod.GetMarketHours) + { + // YES: Get the server GMT time + DateTime serverGMTTime = TimeZoneInfo.ConvertTimeToUtc(DateTime.Parse(response.Headers["Date"])); + + // Convert to OLE format + double oleDateTime = serverGMTTime.ToOADate(); + + // Combine server time and result content + resultContent = $"{oleDateTime}##{resultContent}"; + } + else + // Was an order just placed? + if (apiMethod == ApiMethod.PlaceOrder) + { + // YES: The order number is the last URL segment, + // get it. First create a new URI. + Uri uri = new Uri(response.Headers.GetValues("Location")[0]); + + // Place the last segment of the above URI into the content + resultContent = uri.Segments.Last(); + } + } + else + { + // NO: Return the status error description + resultContent = "ERROR: " + response.StatusDescription; + } + } + } + catch(Exception e) + { + // Error has occurred, capture the error message in result + resultContent = "ERROR: " + e.Message; + } + + // Return the result to the caller + return resultContent; + } + #endregion PUBLIC METHODS + } +} \ No newline at end of file diff --git a/TDAmeritradeZorro/packages.config b/TDAmeritradeZorro/packages.config new file mode 100644 index 0000000..ae4a5d9 --- /dev/null +++ b/TDAmeritradeZorro/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file