├── .gitignore
├── .gitmodules
├── Docs
└── Images
│ ├── EditStrategy.png
│ ├── EquityResults.png
│ ├── MachineLearning.png
│ ├── TradeChart.png
│ └── ViewAllTrades.png
├── LICENSE
├── README.md
└── Src
├── AutomatedTrading.sln
├── Readme.txt
├── StrategyEditor
├── App.config
├── App.xaml
├── App.xaml.cs
├── AppSettings.json
├── AssemblyInfo.cs
├── AssetBalanceUpdater.cs
├── BinanceCandlesUpdater.cs
├── DefaultStrategy.cs
├── DefaultStrategyCrypto.cs
├── ML
│ ├── DataGenerator.cs
│ ├── TensorFlowTextWriter.cs
│ └── Trainer.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── Services
│ ├── StrategyRunnerResultsService.cs
│ └── UIService.cs
├── Settings.Designer.cs
├── Settings.settings
├── StrategyEditor.csproj
├── ViewModels
│ ├── CreatePointViewModel.cs
│ ├── LoginOutViewModel.cs
│ ├── LoginViewModel.cs
│ ├── MachineLearningChartViewModel.cs
│ ├── MachineLearningViewModel.cs
│ ├── MainWindowsViewModel.cs
│ ├── StrategyRunResultsChartViewModel.cs
│ ├── StrategyRunResultsViewModel.cs
│ ├── StrategyRunViewModel.cs
│ └── StrategyViewAllTradesViewModel.cs
├── Views
│ ├── CreatePointGroupView.xaml
│ ├── CreatePointGroupView.xaml.cs
│ ├── CreatePointView.xaml
│ ├── CreatePointView.xaml.cs
│ ├── LoginView.xaml
│ ├── LoginView.xaml.cs
│ ├── MachineLearningView.xaml
│ ├── MachineLearningView.xaml.cs
│ ├── ProgressView.xaml
│ ├── ProgressView.xaml.cs
│ ├── StrategyRunResultsChartView.xaml
│ ├── StrategyRunResultsChartView.xaml.cs
│ ├── StrategyRunResultsView.xaml
│ ├── StrategyRunResultsView.xaml.cs
│ ├── StrategyRunView.xaml
│ ├── StrategyRunView.xaml.cs
│ ├── StrategyViewAllTradesView.xaml
│ └── StrategyViewAllTradesView.xaml.cs
└── log4net.config
├── StrategyRunnerLive
├── App.xaml
├── App.xaml.cs
├── AppSettings.json
├── AssemblyInfo.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── Settings.Designer.cs
├── Settings.settings
├── StrategyRunnerLive.csproj
├── ViewModels
│ ├── LoginOutViewModel.cs
│ ├── LoginViewModel.cs
│ ├── MainWindowViewModel.cs
│ └── RunStrategyLiveViewModel.cs
├── Views
│ ├── LoginView.xaml
│ ├── LoginView.xaml.cs
│ ├── ProgressView.xaml
│ └── ProgressView.xaml.cs
└── log4net.config
└── TraderTools.Simulation.Test
├── TradeAmountUpdaterTests.cs
└── TraderTools.Simulation.Test.csproj
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015/2017 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # Visual Studio 2017 auto generated files
33 | Generated\ Files/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # Benchmark Results
49 | BenchmarkDotNet.Artifacts/
50 |
51 | # .NET Core
52 | project.lock.json
53 | project.fragment.lock.json
54 | artifacts/
55 | **/Properties/launchSettings.json
56 |
57 | # StyleCop
58 | StyleCopReport.xml
59 |
60 | # Files built by Visual Studio
61 | *_i.c
62 | *_p.c
63 | *_i.h
64 | *.ilk
65 | *.meta
66 | *.obj
67 | *.iobj
68 | *.pch
69 | *.pdb
70 | *.ipdb
71 | *.pgc
72 | *.pgd
73 | *.rsp
74 | *.sbr
75 | *.tlb
76 | *.tli
77 | *.tlh
78 | *.tmp
79 | *.tmp_proj
80 | *.log
81 | *.vspscc
82 | *.vssscc
83 | .builds
84 | *.pidb
85 | *.svclog
86 | *.scc
87 |
88 | # Chutzpah Test files
89 | _Chutzpah*
90 |
91 | # Visual C++ cache files
92 | ipch/
93 | *.aps
94 | *.ncb
95 | *.opendb
96 | *.opensdf
97 | *.sdf
98 | *.cachefile
99 | *.VC.db
100 | *.VC.VC.opendb
101 |
102 | # Visual Studio profiler
103 | *.psess
104 | *.vsp
105 | *.vspx
106 | *.sap
107 |
108 | # Visual Studio Trace Files
109 | *.e2e
110 |
111 | # TFS 2012 Local Workspace
112 | $tf/
113 |
114 | # Guidance Automation Toolkit
115 | *.gpState
116 |
117 | # ReSharper is a .NET coding add-in
118 | _ReSharper*/
119 | *.[Rr]e[Ss]harper
120 | *.DotSettings.user
121 |
122 | # JustCode is a .NET coding add-in
123 | .JustCode
124 |
125 | # TeamCity is a build add-in
126 | _TeamCity*
127 |
128 | # DotCover is a Code Coverage Tool
129 | *.dotCover
130 |
131 | # AxoCover is a Code Coverage Tool
132 | .axoCover/*
133 | !.axoCover/settings.json
134 |
135 | # Visual Studio code coverage results
136 | *.coverage
137 | *.coveragexml
138 |
139 | # NCrunch
140 | _NCrunch_*
141 | .*crunch*.local.xml
142 | nCrunchTemp_*
143 |
144 | # MightyMoose
145 | *.mm.*
146 | AutoTest.Net/
147 |
148 | # Web workbench (sass)
149 | .sass-cache/
150 |
151 | # Installshield output folder
152 | [Ee]xpress/
153 |
154 | # DocProject is a documentation generator add-in
155 | DocProject/buildhelp/
156 | DocProject/Help/*.HxT
157 | DocProject/Help/*.HxC
158 | DocProject/Help/*.hhc
159 | DocProject/Help/*.hhk
160 | DocProject/Help/*.hhp
161 | DocProject/Help/Html2
162 | DocProject/Help/html
163 |
164 | # Click-Once directory
165 | publish/
166 |
167 | # Publish Web Output
168 | *.[Pp]ublish.xml
169 | *.azurePubxml
170 | # Note: Comment the next line if you want to checkin your web deploy settings,
171 | # but database connection strings (with potential passwords) will be unencrypted
172 | *.pubxml
173 | *.publishproj
174 |
175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
176 | # checkin your Azure Web App publish settings, but sensitive information contained
177 | # in these scripts will be unencrypted
178 | PublishScripts/
179 |
180 | # NuGet Packages
181 | *.nupkg
182 | # The packages folder can be ignored because of Package Restore
183 | **/[Pp]ackages/*
184 | # except build/, which is used as an MSBuild target.
185 | !**/[Pp]ackages/build/
186 | # Uncomment if necessary however generally it will be regenerated when needed
187 | #!**/[Pp]ackages/repositories.config
188 | # NuGet v3's project.json files produces more ignorable files
189 | *.nuget.props
190 | *.nuget.targets
191 |
192 | # Microsoft Azure Build Output
193 | csx/
194 | *.build.csdef
195 |
196 | # Microsoft Azure Emulator
197 | ecf/
198 | rcf/
199 |
200 | # Windows Store app package directories and files
201 | AppPackages/
202 | BundleArtifacts/
203 | Package.StoreAssociation.xml
204 | _pkginfo.txt
205 | *.appx
206 |
207 | # Visual Studio cache files
208 | # files ending in .cache can be ignored
209 | *.[Cc]ache
210 | # but keep track of directories ending in .cache
211 | !*.[Cc]ache/
212 |
213 | # Others
214 | ClientBin/
215 | ~$*
216 | *~
217 | *.dbmdl
218 | *.dbproj.schemaview
219 | *.jfm
220 | *.pfx
221 | *.publishsettings
222 | orleans.codegen.cs
223 |
224 | # Including strong name files can present a security risk
225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
226 | #*.snk
227 |
228 | # Since there are multiple workflows, uncomment next line to ignore bower_components
229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
230 | #bower_components/
231 |
232 | # RIA/Silverlight projects
233 | Generated_Code/
234 |
235 | # Backup & report files from converting an old project file
236 | # to a newer Visual Studio version. Backup files are not needed,
237 | # because we have git ;-)
238 | _UpgradeReport_Files/
239 | Backup*/
240 | UpgradeLog*.XML
241 | UpgradeLog*.htm
242 | ServiceFabricBackup/
243 | *.rptproj.bak
244 |
245 | # SQL Server files
246 | *.mdf
247 | *.ldf
248 | *.ndf
249 |
250 | # Business Intelligence projects
251 | *.rdl.data
252 | *.bim.layout
253 | *.bim_*.settings
254 | *.rptproj.rsuser
255 |
256 | # Microsoft Fakes
257 | FakesAssemblies/
258 |
259 | # GhostDoc plugin setting file
260 | *.GhostDoc.xml
261 |
262 | # Node.js Tools for Visual Studio
263 | .ntvs_analysis.dat
264 | node_modules/
265 |
266 | # Visual Studio 6 build log
267 | *.plg
268 |
269 | # Visual Studio 6 workspace options file
270 | *.opt
271 |
272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
273 | *.vbw
274 |
275 | # Visual Studio LightSwitch build output
276 | **/*.HTMLClient/GeneratedArtifacts
277 | **/*.DesktopClient/GeneratedArtifacts
278 | **/*.DesktopClient/ModelManifest.xml
279 | **/*.Server/GeneratedArtifacts
280 | **/*.Server/ModelManifest.xml
281 | _Pvt_Extensions
282 |
283 | # Paket dependency manager
284 | .paket/paket.exe
285 | paket-files/
286 |
287 | # FAKE - F# Make
288 | .fake/
289 |
290 | # JetBrains Rider
291 | .idea/
292 | *.sln.iml
293 |
294 | # CodeRush
295 | .cr/
296 |
297 | # Python Tools for Visual Studio (PTVS)
298 | __pycache__/
299 | *.pyc
300 |
301 | # Cake - Uncomment if you are using it
302 | # tools/**
303 | # !tools/packages.config
304 |
305 | # Tabs Studio
306 | *.tss
307 |
308 | # Telerik's JustMock configuration file
309 | *.jmconfig
310 |
311 | # BizTalk build output
312 | *.btp.cs
313 | *.btm.cs
314 | *.odx.cs
315 | *.xsd.cs
316 |
317 | # OpenCover UI analysis results
318 | OpenCover/
319 |
320 | # Azure Stream Analytics local run output
321 | ASALocalRun/
322 |
323 | # MSBuild Binary and Structured Log
324 | *.binlog
325 |
326 | # NVidia Nsight GPU debugger configuration file
327 | *.nvuser
328 |
329 | # MFractors (Xamarin productivity tool) working folder
330 | .mfractor/
331 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "Common"]
2 | path = Common
3 | url = https://github.com/Hallupa/CommonCode
4 |
--------------------------------------------------------------------------------
/Docs/Images/EditStrategy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hallupa/AutomatedTrading/a89d6fbd0a114b9eca3bdaaa60142bc4f3d32800/Docs/Images/EditStrategy.png
--------------------------------------------------------------------------------
/Docs/Images/EquityResults.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hallupa/AutomatedTrading/a89d6fbd0a114b9eca3bdaaa60142bc4f3d32800/Docs/Images/EquityResults.png
--------------------------------------------------------------------------------
/Docs/Images/MachineLearning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hallupa/AutomatedTrading/a89d6fbd0a114b9eca3bdaaa60142bc4f3d32800/Docs/Images/MachineLearning.png
--------------------------------------------------------------------------------
/Docs/Images/TradeChart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hallupa/AutomatedTrading/a89d6fbd0a114b9eca3bdaaa60142bc4f3d32800/Docs/Images/TradeChart.png
--------------------------------------------------------------------------------
/Docs/Images/ViewAllTrades.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hallupa/AutomatedTrading/a89d6fbd0a114b9eca3bdaaa60142bc4f3d32800/Docs/Images/ViewAllTrades.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Note this project is a work-in-progress - there's still lots to be done
2 |
3 | # Trade Bot designer and runner
4 |
5 | This is created in C# in Visual Studio 2019. It is an experimental work-in-progress to create an application to design crypto or spread betting trading bots.
6 | It also will run then live (Currently only with FXCM).
7 |
8 | ## Project aims
9 | 1. Create high performance desktop applicate
10 | 2. Make designing strategies as easy as possible
11 | 3. Ability to design trading bot strategies that run against 1 hour candles and up
12 | 4. Run strategies against fast - i.e. 30 markets with 10 years of data in 30 seconds
13 | 5. Ability to view trades on a candle chart
14 | 6. Show statistics such as expectancy, drawdown, etc
15 | 7. Ability to download candle data from FXCM
16 | 8. Ability to run the strategy live against FXCM
17 | 9. Add machine learning support
18 |
19 | ## Progress so far
20 | This is still highly experimental but a lot of work has been done so far.
21 |
22 | At present I'm just focusing on making this useful for me but if others are interested in this also, I will take feedback and maybe add an installer.
23 | Any feedback or ideas are welcome!
24 |
25 | ## Strategy editor screen
26 | 
27 | On the right is the strategy editor section - this shows a strategy created for placing random trades.
28 | Coding strategies has been designed to be as simple as possible as shown in the screenshot.
29 | The log at the bottom of the screenshot demonstrates just how fast a strategy can run - 33 markets run in 35 seconds creating 105k trades over the 10 years of data.
30 |
31 | ## Trade chart
32 | 
33 | This shows all the trades found for the strategy - these can then be shown on the trade chart.
34 |
35 | ## View all trades
36 | 
37 | View all trades on a single chart.
38 |
39 | ## Equity chart
40 | 
41 | This is the chart of equity over the time of the simulated trades. This is the random trades strategy which results in the account dropping to zero.
42 |
43 | ## Machine learning
44 | 
45 | Set points of interest on the chart which will then feed into the TensorFlow machine learning, which will use a neural network to learn the data.
46 |
--------------------------------------------------------------------------------
/Src/AutomatedTrading.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.32014.148
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{40142F79-164E-4933-9088-356B51B82235}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Library.Test", "..\Common\Src\Library.Test\Library.Test.csproj", "{746EC390-FDA6-4CB3-AC5C-B59D0B198701}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TraderTools.Simulation.Test", "TraderTools.Simulation.Test\TraderTools.Simulation.Test.csproj", "{3799A397-C8CE-49BF-B118-4446FC3A64F3}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StrategyEditor", "StrategyEditor\StrategyEditor.csproj", "{253BDA9E-1A0B-4F89-AD15-EEEA50573832}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StrategyRunnerLive", "StrategyRunnerLive\StrategyRunnerLive.csproj", "{A1535BD5-EC9A-4B70-BB47-02DBC0257136}"
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Library.UI", "..\..\CommonCode\Src\Library.UI\Library.UI.csproj", "{09B93F55-95A7-4147-B926-951B58775EA6}"
17 | EndProject
18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documents", "Documents", "{EB6C66A6-7731-493D-8158-D78B7AB5CB01}"
19 | ProjectSection(SolutionItems) = preProject
20 | Readme.txt = Readme.txt
21 | EndProjectSection
22 | EndProject
23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Library", "..\..\CommonCode\Src\Library\Library.csproj", "{E2845BA3-4094-4AAB-B1E7-1444CE8D70D8}"
24 | EndProject
25 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TraderTools.Basics", "..\..\CommonCode\Src\TraderTools.Basics\TraderTools.Basics.csproj", "{4FA5B271-12DC-41B3-B479-84527D6E9E4B}"
26 | EndProject
27 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TraderTools.Core.UI", "..\..\CommonCode\Src\TraderTools.Core.UI\TraderTools.Core.UI.csproj", "{039D924F-F6EE-45F3-8628-21942A0B2BB8}"
28 | EndProject
29 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TraderTools.Indicators", "..\..\CommonCode\Src\TraderTools.Indicators\TraderTools.Indicators.csproj", "{926B0411-B617-4BC4-AFED-05C1DF113612}"
30 | EndProject
31 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TraderTools.Brokers.Binance", "..\..\CommonCode\Src\TraderTools.Brokers.Binance\TraderTools.Brokers.Binance.csproj", "{E8CD1F79-CA8D-4248-9F25-871003C5848A}"
32 | EndProject
33 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TraderTools.Brokers.FXCM", "..\..\CommonCode\Src\TraderTools.Brokers.FXCM\TraderTools.Brokers.FXCM.csproj", "{34539E29-D398-49D6-81D6-97ACC212C4C1}"
34 | EndProject
35 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TraderTools.Core", "..\..\CommonCode\Src\TraderTools.Core\TraderTools.Core.csproj", "{75F6029E-5DBB-4BCA-8E96-623955945D46}"
36 | EndProject
37 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TraderTools.Simulation", "..\..\CommonCode\Src\TraderTools.Simulation\TraderTools.Simulation.csproj", "{0B8ADC68-8DEC-4641-A1AF-6AC445A4EBBB}"
38 | EndProject
39 | Global
40 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
41 | Debug|Any CPU = Debug|Any CPU
42 | Debug|x64 = Debug|x64
43 | Debug|x86 = Debug|x86
44 | Release|Any CPU = Release|Any CPU
45 | Release|x64 = Release|x64
46 | Release|x86 = Release|x86
47 | EndGlobalSection
48 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
49 | {746EC390-FDA6-4CB3-AC5C-B59D0B198701}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50 | {746EC390-FDA6-4CB3-AC5C-B59D0B198701}.Debug|Any CPU.Build.0 = Debug|Any CPU
51 | {746EC390-FDA6-4CB3-AC5C-B59D0B198701}.Debug|x64.ActiveCfg = Debug|x64
52 | {746EC390-FDA6-4CB3-AC5C-B59D0B198701}.Debug|x64.Build.0 = Debug|x64
53 | {746EC390-FDA6-4CB3-AC5C-B59D0B198701}.Debug|x86.ActiveCfg = Debug|Any CPU
54 | {746EC390-FDA6-4CB3-AC5C-B59D0B198701}.Debug|x86.Build.0 = Debug|Any CPU
55 | {746EC390-FDA6-4CB3-AC5C-B59D0B198701}.Release|Any CPU.ActiveCfg = Release|Any CPU
56 | {746EC390-FDA6-4CB3-AC5C-B59D0B198701}.Release|Any CPU.Build.0 = Release|Any CPU
57 | {746EC390-FDA6-4CB3-AC5C-B59D0B198701}.Release|x64.ActiveCfg = Release|Any CPU
58 | {746EC390-FDA6-4CB3-AC5C-B59D0B198701}.Release|x64.Build.0 = Release|Any CPU
59 | {746EC390-FDA6-4CB3-AC5C-B59D0B198701}.Release|x86.ActiveCfg = Release|Any CPU
60 | {746EC390-FDA6-4CB3-AC5C-B59D0B198701}.Release|x86.Build.0 = Release|Any CPU
61 | {3799A397-C8CE-49BF-B118-4446FC3A64F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
62 | {3799A397-C8CE-49BF-B118-4446FC3A64F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
63 | {3799A397-C8CE-49BF-B118-4446FC3A64F3}.Debug|x64.ActiveCfg = Debug|x64
64 | {3799A397-C8CE-49BF-B118-4446FC3A64F3}.Debug|x64.Build.0 = Debug|x64
65 | {3799A397-C8CE-49BF-B118-4446FC3A64F3}.Debug|x86.ActiveCfg = Debug|Any CPU
66 | {3799A397-C8CE-49BF-B118-4446FC3A64F3}.Debug|x86.Build.0 = Debug|Any CPU
67 | {3799A397-C8CE-49BF-B118-4446FC3A64F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
68 | {3799A397-C8CE-49BF-B118-4446FC3A64F3}.Release|Any CPU.Build.0 = Release|Any CPU
69 | {3799A397-C8CE-49BF-B118-4446FC3A64F3}.Release|x64.ActiveCfg = Release|Any CPU
70 | {3799A397-C8CE-49BF-B118-4446FC3A64F3}.Release|x64.Build.0 = Release|Any CPU
71 | {3799A397-C8CE-49BF-B118-4446FC3A64F3}.Release|x86.ActiveCfg = Release|Any CPU
72 | {3799A397-C8CE-49BF-B118-4446FC3A64F3}.Release|x86.Build.0 = Release|Any CPU
73 | {253BDA9E-1A0B-4F89-AD15-EEEA50573832}.Debug|Any CPU.ActiveCfg = Debug|x64
74 | {253BDA9E-1A0B-4F89-AD15-EEEA50573832}.Debug|Any CPU.Build.0 = Debug|x64
75 | {253BDA9E-1A0B-4F89-AD15-EEEA50573832}.Debug|x64.ActiveCfg = Debug|x64
76 | {253BDA9E-1A0B-4F89-AD15-EEEA50573832}.Debug|x64.Build.0 = Debug|x64
77 | {253BDA9E-1A0B-4F89-AD15-EEEA50573832}.Debug|x86.ActiveCfg = Debug|Any CPU
78 | {253BDA9E-1A0B-4F89-AD15-EEEA50573832}.Debug|x86.Build.0 = Debug|Any CPU
79 | {253BDA9E-1A0B-4F89-AD15-EEEA50573832}.Release|Any CPU.ActiveCfg = Release|Any CPU
80 | {253BDA9E-1A0B-4F89-AD15-EEEA50573832}.Release|Any CPU.Build.0 = Release|Any CPU
81 | {253BDA9E-1A0B-4F89-AD15-EEEA50573832}.Release|x64.ActiveCfg = Release|x64
82 | {253BDA9E-1A0B-4F89-AD15-EEEA50573832}.Release|x64.Build.0 = Release|x64
83 | {253BDA9E-1A0B-4F89-AD15-EEEA50573832}.Release|x86.ActiveCfg = Release|Any CPU
84 | {253BDA9E-1A0B-4F89-AD15-EEEA50573832}.Release|x86.Build.0 = Release|Any CPU
85 | {A1535BD5-EC9A-4B70-BB47-02DBC0257136}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
86 | {A1535BD5-EC9A-4B70-BB47-02DBC0257136}.Debug|Any CPU.Build.0 = Debug|Any CPU
87 | {A1535BD5-EC9A-4B70-BB47-02DBC0257136}.Debug|x64.ActiveCfg = Debug|x64
88 | {A1535BD5-EC9A-4B70-BB47-02DBC0257136}.Debug|x64.Build.0 = Debug|x64
89 | {A1535BD5-EC9A-4B70-BB47-02DBC0257136}.Debug|x86.ActiveCfg = Debug|Any CPU
90 | {A1535BD5-EC9A-4B70-BB47-02DBC0257136}.Debug|x86.Build.0 = Debug|Any CPU
91 | {A1535BD5-EC9A-4B70-BB47-02DBC0257136}.Release|Any CPU.ActiveCfg = Release|Any CPU
92 | {A1535BD5-EC9A-4B70-BB47-02DBC0257136}.Release|Any CPU.Build.0 = Release|Any CPU
93 | {A1535BD5-EC9A-4B70-BB47-02DBC0257136}.Release|x64.ActiveCfg = Release|Any CPU
94 | {A1535BD5-EC9A-4B70-BB47-02DBC0257136}.Release|x64.Build.0 = Release|Any CPU
95 | {A1535BD5-EC9A-4B70-BB47-02DBC0257136}.Release|x86.ActiveCfg = Release|Any CPU
96 | {A1535BD5-EC9A-4B70-BB47-02DBC0257136}.Release|x86.Build.0 = Release|Any CPU
97 | {09B93F55-95A7-4147-B926-951B58775EA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
98 | {09B93F55-95A7-4147-B926-951B58775EA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
99 | {09B93F55-95A7-4147-B926-951B58775EA6}.Debug|x64.ActiveCfg = Debug|x64
100 | {09B93F55-95A7-4147-B926-951B58775EA6}.Debug|x64.Build.0 = Debug|x64
101 | {09B93F55-95A7-4147-B926-951B58775EA6}.Debug|x86.ActiveCfg = Debug|Any CPU
102 | {09B93F55-95A7-4147-B926-951B58775EA6}.Debug|x86.Build.0 = Debug|Any CPU
103 | {09B93F55-95A7-4147-B926-951B58775EA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
104 | {09B93F55-95A7-4147-B926-951B58775EA6}.Release|Any CPU.Build.0 = Release|Any CPU
105 | {09B93F55-95A7-4147-B926-951B58775EA6}.Release|x64.ActiveCfg = Release|Any CPU
106 | {09B93F55-95A7-4147-B926-951B58775EA6}.Release|x64.Build.0 = Release|Any CPU
107 | {09B93F55-95A7-4147-B926-951B58775EA6}.Release|x86.ActiveCfg = Release|Any CPU
108 | {09B93F55-95A7-4147-B926-951B58775EA6}.Release|x86.Build.0 = Release|Any CPU
109 | {E2845BA3-4094-4AAB-B1E7-1444CE8D70D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
110 | {E2845BA3-4094-4AAB-B1E7-1444CE8D70D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
111 | {E2845BA3-4094-4AAB-B1E7-1444CE8D70D8}.Debug|x64.ActiveCfg = Debug|x64
112 | {E2845BA3-4094-4AAB-B1E7-1444CE8D70D8}.Debug|x64.Build.0 = Debug|x64
113 | {E2845BA3-4094-4AAB-B1E7-1444CE8D70D8}.Debug|x86.ActiveCfg = Debug|Any CPU
114 | {E2845BA3-4094-4AAB-B1E7-1444CE8D70D8}.Debug|x86.Build.0 = Debug|Any CPU
115 | {E2845BA3-4094-4AAB-B1E7-1444CE8D70D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
116 | {E2845BA3-4094-4AAB-B1E7-1444CE8D70D8}.Release|Any CPU.Build.0 = Release|Any CPU
117 | {E2845BA3-4094-4AAB-B1E7-1444CE8D70D8}.Release|x64.ActiveCfg = Release|Any CPU
118 | {E2845BA3-4094-4AAB-B1E7-1444CE8D70D8}.Release|x64.Build.0 = Release|Any CPU
119 | {E2845BA3-4094-4AAB-B1E7-1444CE8D70D8}.Release|x86.ActiveCfg = Release|Any CPU
120 | {E2845BA3-4094-4AAB-B1E7-1444CE8D70D8}.Release|x86.Build.0 = Release|Any CPU
121 | {4FA5B271-12DC-41B3-B479-84527D6E9E4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
122 | {4FA5B271-12DC-41B3-B479-84527D6E9E4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
123 | {4FA5B271-12DC-41B3-B479-84527D6E9E4B}.Debug|x64.ActiveCfg = Debug|x64
124 | {4FA5B271-12DC-41B3-B479-84527D6E9E4B}.Debug|x64.Build.0 = Debug|x64
125 | {4FA5B271-12DC-41B3-B479-84527D6E9E4B}.Debug|x86.ActiveCfg = Debug|Any CPU
126 | {4FA5B271-12DC-41B3-B479-84527D6E9E4B}.Debug|x86.Build.0 = Debug|Any CPU
127 | {4FA5B271-12DC-41B3-B479-84527D6E9E4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
128 | {4FA5B271-12DC-41B3-B479-84527D6E9E4B}.Release|Any CPU.Build.0 = Release|Any CPU
129 | {4FA5B271-12DC-41B3-B479-84527D6E9E4B}.Release|x64.ActiveCfg = Release|Any CPU
130 | {4FA5B271-12DC-41B3-B479-84527D6E9E4B}.Release|x64.Build.0 = Release|Any CPU
131 | {4FA5B271-12DC-41B3-B479-84527D6E9E4B}.Release|x86.ActiveCfg = Release|Any CPU
132 | {4FA5B271-12DC-41B3-B479-84527D6E9E4B}.Release|x86.Build.0 = Release|Any CPU
133 | {039D924F-F6EE-45F3-8628-21942A0B2BB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
134 | {039D924F-F6EE-45F3-8628-21942A0B2BB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
135 | {039D924F-F6EE-45F3-8628-21942A0B2BB8}.Debug|x64.ActiveCfg = Debug|x64
136 | {039D924F-F6EE-45F3-8628-21942A0B2BB8}.Debug|x64.Build.0 = Debug|x64
137 | {039D924F-F6EE-45F3-8628-21942A0B2BB8}.Debug|x86.ActiveCfg = Debug|Any CPU
138 | {039D924F-F6EE-45F3-8628-21942A0B2BB8}.Debug|x86.Build.0 = Debug|Any CPU
139 | {039D924F-F6EE-45F3-8628-21942A0B2BB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
140 | {039D924F-F6EE-45F3-8628-21942A0B2BB8}.Release|Any CPU.Build.0 = Release|Any CPU
141 | {039D924F-F6EE-45F3-8628-21942A0B2BB8}.Release|x64.ActiveCfg = Release|Any CPU
142 | {039D924F-F6EE-45F3-8628-21942A0B2BB8}.Release|x64.Build.0 = Release|Any CPU
143 | {039D924F-F6EE-45F3-8628-21942A0B2BB8}.Release|x86.ActiveCfg = Release|Any CPU
144 | {039D924F-F6EE-45F3-8628-21942A0B2BB8}.Release|x86.Build.0 = Release|Any CPU
145 | {926B0411-B617-4BC4-AFED-05C1DF113612}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
146 | {926B0411-B617-4BC4-AFED-05C1DF113612}.Debug|Any CPU.Build.0 = Debug|Any CPU
147 | {926B0411-B617-4BC4-AFED-05C1DF113612}.Debug|x64.ActiveCfg = Debug|x64
148 | {926B0411-B617-4BC4-AFED-05C1DF113612}.Debug|x64.Build.0 = Debug|x64
149 | {926B0411-B617-4BC4-AFED-05C1DF113612}.Debug|x86.ActiveCfg = Debug|Any CPU
150 | {926B0411-B617-4BC4-AFED-05C1DF113612}.Debug|x86.Build.0 = Debug|Any CPU
151 | {926B0411-B617-4BC4-AFED-05C1DF113612}.Release|Any CPU.ActiveCfg = Release|Any CPU
152 | {926B0411-B617-4BC4-AFED-05C1DF113612}.Release|Any CPU.Build.0 = Release|Any CPU
153 | {926B0411-B617-4BC4-AFED-05C1DF113612}.Release|x64.ActiveCfg = Release|Any CPU
154 | {926B0411-B617-4BC4-AFED-05C1DF113612}.Release|x64.Build.0 = Release|Any CPU
155 | {926B0411-B617-4BC4-AFED-05C1DF113612}.Release|x86.ActiveCfg = Release|Any CPU
156 | {926B0411-B617-4BC4-AFED-05C1DF113612}.Release|x86.Build.0 = Release|Any CPU
157 | {E8CD1F79-CA8D-4248-9F25-871003C5848A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
158 | {E8CD1F79-CA8D-4248-9F25-871003C5848A}.Debug|Any CPU.Build.0 = Debug|Any CPU
159 | {E8CD1F79-CA8D-4248-9F25-871003C5848A}.Debug|x64.ActiveCfg = Debug|x64
160 | {E8CD1F79-CA8D-4248-9F25-871003C5848A}.Debug|x64.Build.0 = Debug|x64
161 | {E8CD1F79-CA8D-4248-9F25-871003C5848A}.Debug|x86.ActiveCfg = Debug|Any CPU
162 | {E8CD1F79-CA8D-4248-9F25-871003C5848A}.Debug|x86.Build.0 = Debug|Any CPU
163 | {E8CD1F79-CA8D-4248-9F25-871003C5848A}.Release|Any CPU.ActiveCfg = Release|Any CPU
164 | {E8CD1F79-CA8D-4248-9F25-871003C5848A}.Release|Any CPU.Build.0 = Release|Any CPU
165 | {E8CD1F79-CA8D-4248-9F25-871003C5848A}.Release|x64.ActiveCfg = Release|Any CPU
166 | {E8CD1F79-CA8D-4248-9F25-871003C5848A}.Release|x64.Build.0 = Release|Any CPU
167 | {E8CD1F79-CA8D-4248-9F25-871003C5848A}.Release|x86.ActiveCfg = Release|Any CPU
168 | {E8CD1F79-CA8D-4248-9F25-871003C5848A}.Release|x86.Build.0 = Release|Any CPU
169 | {34539E29-D398-49D6-81D6-97ACC212C4C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
170 | {34539E29-D398-49D6-81D6-97ACC212C4C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
171 | {34539E29-D398-49D6-81D6-97ACC212C4C1}.Debug|x64.ActiveCfg = Debug|x64
172 | {34539E29-D398-49D6-81D6-97ACC212C4C1}.Debug|x64.Build.0 = Debug|x64
173 | {34539E29-D398-49D6-81D6-97ACC212C4C1}.Debug|x86.ActiveCfg = Debug|Any CPU
174 | {34539E29-D398-49D6-81D6-97ACC212C4C1}.Debug|x86.Build.0 = Debug|Any CPU
175 | {34539E29-D398-49D6-81D6-97ACC212C4C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
176 | {34539E29-D398-49D6-81D6-97ACC212C4C1}.Release|Any CPU.Build.0 = Release|Any CPU
177 | {34539E29-D398-49D6-81D6-97ACC212C4C1}.Release|x64.ActiveCfg = Release|Any CPU
178 | {34539E29-D398-49D6-81D6-97ACC212C4C1}.Release|x64.Build.0 = Release|Any CPU
179 | {34539E29-D398-49D6-81D6-97ACC212C4C1}.Release|x86.ActiveCfg = Release|Any CPU
180 | {34539E29-D398-49D6-81D6-97ACC212C4C1}.Release|x86.Build.0 = Release|Any CPU
181 | {75F6029E-5DBB-4BCA-8E96-623955945D46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
182 | {75F6029E-5DBB-4BCA-8E96-623955945D46}.Debug|Any CPU.Build.0 = Debug|Any CPU
183 | {75F6029E-5DBB-4BCA-8E96-623955945D46}.Debug|x64.ActiveCfg = Debug|x64
184 | {75F6029E-5DBB-4BCA-8E96-623955945D46}.Debug|x64.Build.0 = Debug|x64
185 | {75F6029E-5DBB-4BCA-8E96-623955945D46}.Debug|x86.ActiveCfg = Debug|Any CPU
186 | {75F6029E-5DBB-4BCA-8E96-623955945D46}.Debug|x86.Build.0 = Debug|Any CPU
187 | {75F6029E-5DBB-4BCA-8E96-623955945D46}.Release|Any CPU.ActiveCfg = Release|Any CPU
188 | {75F6029E-5DBB-4BCA-8E96-623955945D46}.Release|Any CPU.Build.0 = Release|Any CPU
189 | {75F6029E-5DBB-4BCA-8E96-623955945D46}.Release|x64.ActiveCfg = Release|Any CPU
190 | {75F6029E-5DBB-4BCA-8E96-623955945D46}.Release|x64.Build.0 = Release|Any CPU
191 | {75F6029E-5DBB-4BCA-8E96-623955945D46}.Release|x86.ActiveCfg = Release|Any CPU
192 | {75F6029E-5DBB-4BCA-8E96-623955945D46}.Release|x86.Build.0 = Release|Any CPU
193 | {0B8ADC68-8DEC-4641-A1AF-6AC445A4EBBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
194 | {0B8ADC68-8DEC-4641-A1AF-6AC445A4EBBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
195 | {0B8ADC68-8DEC-4641-A1AF-6AC445A4EBBB}.Debug|x64.ActiveCfg = Debug|x64
196 | {0B8ADC68-8DEC-4641-A1AF-6AC445A4EBBB}.Debug|x64.Build.0 = Debug|x64
197 | {0B8ADC68-8DEC-4641-A1AF-6AC445A4EBBB}.Debug|x86.ActiveCfg = Debug|Any CPU
198 | {0B8ADC68-8DEC-4641-A1AF-6AC445A4EBBB}.Debug|x86.Build.0 = Debug|Any CPU
199 | {0B8ADC68-8DEC-4641-A1AF-6AC445A4EBBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
200 | {0B8ADC68-8DEC-4641-A1AF-6AC445A4EBBB}.Release|Any CPU.Build.0 = Release|Any CPU
201 | {0B8ADC68-8DEC-4641-A1AF-6AC445A4EBBB}.Release|x64.ActiveCfg = Release|Any CPU
202 | {0B8ADC68-8DEC-4641-A1AF-6AC445A4EBBB}.Release|x64.Build.0 = Release|Any CPU
203 | {0B8ADC68-8DEC-4641-A1AF-6AC445A4EBBB}.Release|x86.ActiveCfg = Release|Any CPU
204 | {0B8ADC68-8DEC-4641-A1AF-6AC445A4EBBB}.Release|x86.Build.0 = Release|Any CPU
205 | EndGlobalSection
206 | GlobalSection(SolutionProperties) = preSolution
207 | HideSolutionNode = FALSE
208 | EndGlobalSection
209 | GlobalSection(NestedProjects) = preSolution
210 | {746EC390-FDA6-4CB3-AC5C-B59D0B198701} = {40142F79-164E-4933-9088-356B51B82235}
211 | {3799A397-C8CE-49BF-B118-4446FC3A64F3} = {40142F79-164E-4933-9088-356B51B82235}
212 | {09B93F55-95A7-4147-B926-951B58775EA6} = {40142F79-164E-4933-9088-356B51B82235}
213 | {E2845BA3-4094-4AAB-B1E7-1444CE8D70D8} = {40142F79-164E-4933-9088-356B51B82235}
214 | {4FA5B271-12DC-41B3-B479-84527D6E9E4B} = {40142F79-164E-4933-9088-356B51B82235}
215 | {039D924F-F6EE-45F3-8628-21942A0B2BB8} = {40142F79-164E-4933-9088-356B51B82235}
216 | {926B0411-B617-4BC4-AFED-05C1DF113612} = {40142F79-164E-4933-9088-356B51B82235}
217 | {E8CD1F79-CA8D-4248-9F25-871003C5848A} = {40142F79-164E-4933-9088-356B51B82235}
218 | {34539E29-D398-49D6-81D6-97ACC212C4C1} = {40142F79-164E-4933-9088-356B51B82235}
219 | {75F6029E-5DBB-4BCA-8E96-623955945D46} = {40142F79-164E-4933-9088-356B51B82235}
220 | {0B8ADC68-8DEC-4641-A1AF-6AC445A4EBBB} = {40142F79-164E-4933-9088-356B51B82235}
221 | EndGlobalSection
222 | GlobalSection(ExtensibilityGlobals) = postSolution
223 | SolutionGuid = {0A2E90D5-6948-4DDF-A8A8-4903CCCF8DBA}
224 | EndGlobalSection
225 | EndGlobal
226 |
--------------------------------------------------------------------------------
/Src/Readme.txt:
--------------------------------------------------------------------------------
1 | Install
2 | https://fxcodebase.com/wiki/index.php/Download
3 | https://www.scichart.com/Downloads/v3.5/SciChart_v3.5.7128_Installer.zip
4 |
5 | Running
6 | Has to be run as x64
--------------------------------------------------------------------------------
/Src/StrategyEditor/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.Composition;
2 | using System.IO;
3 | using System.Reflection;
4 | using System.Windows;
5 | using Hallupa.Library;
6 | using log4net;
7 | using log4net.Config;
8 | using TraderTools.Basics;
9 | using TraderTools.Core.Services;
10 | using TraderTools.Core.UI.Services;
11 | using TraderTools.Simulation;
12 |
13 | namespace StrategyEditor
14 | {
15 | ///
16 | /// Interaction logic for App.xaml
17 | ///
18 | public partial class App : Application
19 | {
20 | private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
21 |
22 | [Import] private IDataDirectoryService _dataDirectoryService;
23 |
24 | protected override void OnStartup(StartupEventArgs e)
25 | {
26 | var logRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
27 | XmlConfigurator.Configure(logRepository, new FileInfo("log4net.config"));
28 |
29 | Log.Info("Starting application");
30 |
31 | DependencyContainer.AddAssembly(typeof(App).Assembly);
32 | DependencyContainer.AddAssembly(typeof(BrokersService).Assembly);
33 | DependencyContainer.AddAssembly(typeof(ChartingService).Assembly);
34 | DependencyContainer.AddAssembly(typeof(ModelPredictorService).Assembly);
35 |
36 | DependencyContainer.ComposeParts(this);
37 |
38 | _dataDirectoryService.SetApplicationName("AutomatedTrader");
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/AppSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Binance": {
3 | "ApiKey": "",
4 | "SecretKey": ""
5 | }
6 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | [assembly: ThemeInfo(
4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
5 | //(used if a resource is not found in the page,
6 | // or application resource dictionaries)
7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
8 | //(used if a resource is not found in the page,
9 | // app, or any theme specific resource dictionaries)
10 | )]
11 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/AssetBalanceUpdater.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Hallupa.TraderTools.Basics;
5 | using TraderTools.Basics;
6 |
7 | namespace StrategyEditor
8 | {
9 | internal static class AssetBalanceUpdater
10 | {
11 | public static void UpdateAssetBalance(this Dictionary assetBalances, Trade trade)
12 | {
13 | /* TODO if (trade.TradeDirection == TradeDirection.Long && trade.EntryQuantity != null && trade.EntryPrice != null)
14 | {
15 | // Entry quantity already has commission taken off
16 | assetBalances.AddToAssetBalance(trade.BaseAsset, trade.EntryQuantity.Value);
17 |
18 | assetBalances.AddToAssetBalance(trade.GetQuoteAsset(), -trade.EntryQuantity.Value * trade.EntryPrice.Value);
19 |
20 | if (trade.Commission != null)
21 | {
22 | if (trade.CommissionAsset != trade.GetQuoteAsset())
23 | throw new ApplicationException("Commission asset must be quote asset");
24 |
25 | assetBalances.AddToAssetBalance(trade.GetQuoteAsset(), -trade.Commission.Value);
26 | }
27 | }
28 |
29 | if (trade.TradeDirection == TradeDirection.Short && trade.EntryQuantity != null && trade.EntryPrice != null)
30 | {
31 | // Entry quantity already has commission taken off
32 | assetBalances.AddToAssetBalance(trade.BaseAsset, -trade.EntryQuantity.Value);
33 | assetBalances.AddToAssetBalance(trade.GetQuoteAsset(), trade.EntryQuantity.Value * trade.EntryPrice.Value);
34 |
35 | if (trade.Commission != null)
36 | {
37 | if (trade.CommissionAsset != trade.GetQuoteAsset())
38 | throw new ApplicationException("Commission asset must be quote asset");
39 |
40 | assetBalances.AddToAssetBalance(trade.GetQuoteAsset(), -trade.Commission.Value);
41 | }
42 | }
43 |
44 | if (assetBalances.Any(x => x.Value.Balance < -0.0001M))
45 | {
46 | throw new ApplicationException("Asset balance below zero");
47 | }*/
48 | }
49 |
50 | private static void AddToAssetBalance(this Dictionary assetBalances, string asset, decimal amount)
51 | {
52 | if (!assetBalances.TryGetValue(asset, out var assetBalance))
53 | {
54 | assetBalances[asset] = (assetBalance = new AssetBalance(asset, amount));
55 | return;
56 | }
57 |
58 | assetBalances[asset] = new AssetBalance(asset, assetBalance.Balance + amount);
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/BinanceCandlesUpdater.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Hallupa.Library;
5 | using Hallupa.TraderTools.Brokers.Binance;
6 | using log4net;
7 | using TraderTools.Basics;
8 |
9 | namespace CryptoTrader
10 | {
11 | public class BinanceCandlesUpdater
12 | {
13 | private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
14 | private readonly BinanceBroker _binance;
15 | private readonly IBrokersCandlesService _candlesService;
16 | private readonly IMarketDetailsService _marketDetailsService;
17 |
18 | public BinanceCandlesUpdater(BinanceBroker binance, IBrokersCandlesService candlesService, IMarketDetailsService marketDetailsService)
19 | {
20 | _binance = binance;
21 | _candlesService = candlesService;
22 | _marketDetailsService = marketDetailsService;
23 | }
24 |
25 | public Task RunAsync()
26 | {
27 | return Task.Run(() =>
28 | {
29 | var symbols = _binance.GetSymbols();
30 |
31 | foreach (var s in symbols)
32 | {
33 | _marketDetailsService.AddMarketDetails(new MarketDetails
34 | {
35 | Broker = "Binance",
36 | Name = s
37 | });
38 | }
39 |
40 | _marketDetailsService.SaveMarketDetailsList();
41 |
42 | var added = 0;
43 | var done = 0;
44 | var producerConsumer = new ProducerConsumer<(string Symbol, Timeframe Timeframe)>(
45 | 3,
46 | x =>
47 | {
48 | Log.Info($"Updating candles for {x.Data.Symbol} {x.Data.Timeframe}");
49 | _candlesService.UpdateCandles(_binance, x.Data.Symbol, x.Data.Timeframe);
50 | Interlocked.Increment(ref done);
51 | Log.Info($"Completed candles for {x.Data.Symbol} {x.Data.Timeframe} - total: {((double)done * 100.0) / ((double)added):0.00}%");
52 | return ProducerConsumerActionResult.Success;
53 | });
54 |
55 | foreach (var symbol in symbols)
56 | {
57 | producerConsumer.Add((symbol, Timeframe.H1));
58 | producerConsumer.Add((symbol, Timeframe.H2));
59 | producerConsumer.Add((symbol, Timeframe.H4));
60 | producerConsumer.Add((symbol, Timeframe.H8));
61 | producerConsumer.Add((symbol, Timeframe.D1));
62 |
63 | if (symbol.EndsWith("BNB") || symbol.EndsWith("USDT") || symbol.EndsWith("BTC"))
64 | {
65 | producerConsumer.Add((symbol, Timeframe.M5));
66 | producerConsumer.Add((symbol, Timeframe.M15));
67 | producerConsumer.Add((symbol, Timeframe.M30));
68 | }
69 | }
70 |
71 | added = producerConsumer.QueueLength;
72 | producerConsumer.SetProducerCompleted();
73 | producerConsumer.Start();
74 |
75 | producerConsumer.WaitUntilConsumersFinished();
76 |
77 | // Finish window setup
78 | Log.Info("Symbols updated");
79 | });
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/DefaultStrategy.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Hallupa.TraderTools.Basics;
3 | using Hallupa.TraderTools.Simulation;
4 | using TraderTools.Basics;
5 | using TraderTools.Simulation;
6 |
7 | namespace StrategyEditor
8 | {
9 | public class DefaultStrategy : StrategyBase
10 | {
11 | private readonly IndicatorValues _ema8;
12 | private readonly IndicatorValues _ema21;
13 |
14 | public DefaultStrategy()
15 | {
16 | SetSimulationGranularity(Timeframe.M5);
17 | SetCommission(0.00075M); // With BNB discount
18 | SetBroker("Binance");
19 | SetMarkets("ETHUSDT");
20 | SetTimeframes(Timeframe.H1);
21 | SetSimulationInitialBalance(new AssetBalance("USDT", 10000M));
22 |
23 | _ema21 = EMA("ETHUSDT", Timeframe.H1, 21);
24 | _ema8 = EMA("ETHUSDT", Timeframe.H1, 8);
25 | }
26 |
27 | public override void ProcessCandles(List addedCandleTimeframes)
28 | {
29 | if (!_ema8.IsFormed || !_ema21.IsFormed) return;
30 |
31 | if (_ema8.Value > _ema21.Value)
32 | {
33 | var balance = GetCurrentBalance("USDT");
34 | if (balance <= 0.1M) return;
35 |
36 | MarketBuy("ETHUSDT", "ETH", balance);
37 | }
38 |
39 | if (_ema8.Value < _ema21.Value)
40 | {
41 | var balance = GetCurrentBalance("ETH");
42 | if (balance <= 0.1M) return;
43 |
44 | MarketSell("ETHUSDT", "ETH", balance);
45 | }
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/DefaultStrategyCrypto.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Hallupa.TraderTools.Basics;
3 | using Hallupa.TraderTools.Simulation;
4 | using TraderTools.Basics;
5 | using TraderTools.Simulation;
6 |
7 | namespace StrategyEditor
8 | {
9 | public class ETHUSDT_EMACross : StrategyBase
10 | {
11 | private readonly IndicatorValues _ema8;
12 | private readonly IndicatorValues _ema21;
13 |
14 | public ETHUSDT_EMACross()
15 | {
16 | SetSimulationGranularity(Timeframe.M5);
17 | SetCommission(0.00075M); // With BNB discount
18 | SetBroker("Binance");
19 | SetMarkets("ETHUSDT");
20 | SetTimeframes(Timeframe.H1);
21 | SetSimulationInitialBalance(new AssetBalance("USDT", 10000M));
22 |
23 | _ema21 = EMA("ETHUSDT", Timeframe.H1, 21);
24 | _ema8 = EMA("ETHUSDT", Timeframe.H1, 8);
25 | }
26 |
27 | public override void ProcessCandles(List addedCandleTimeframes)
28 | {
29 | if (!_ema8.IsFormed || !_ema21.IsFormed) return;
30 |
31 | if (_ema8.Value > _ema21.Value)
32 | {
33 | var balance = GetCurrentBalance("USDT");
34 | if (balance <= 0.1M) return;
35 |
36 | MarketBuy("ETHUSDT", "ETH", balance);
37 | }
38 |
39 | if (_ema8.Value < _ema21.Value)
40 | {
41 | var balance = GetCurrentBalance("ETH");
42 | if (balance <= 0.1M) return;
43 |
44 | MarketSell("ETHUSDT", "ETH", balance);
45 | }
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/ML/DataGenerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Hallupa.Library.Extensions;
5 | using StrategyEditor.ViewModels;
6 | using TraderTools.Basics;
7 | using TraderTools.Basics.Extensions;
8 |
9 | namespace StrategyEditor.ML
10 | {
11 | public class DataGenerator
12 | {
13 | private readonly IBrokersCandlesService _candlesService;
14 | private readonly IBrokersService _brokersService;
15 | private const int CandlesCountForDataPoint = 6;
16 |
17 | public DataGenerator(IBrokersCandlesService candlesService, IBrokersService brokersService)
18 | {
19 | _candlesService = candlesService;
20 | _brokersService = brokersService;
21 | }
22 |
23 | public (List x, List y, List candlesUsed) GetPointXYData(MLPoint p, MLPointCollection points)
24 | {
25 | var candlesLookup = GetCandlesLookup(points);
26 | return GetPointXYData(p, candlesLookup);
27 | }
28 |
29 | public (List x, List y, List candlesUsed) GetPointXYData(
30 | MLPoint p,
31 | Dictionary<(string Market, Timeframe Timeframe),
32 | List> candlesLookup)
33 | {
34 | var allCandles = candlesLookup[(p.Market, p.Timeframe)];
35 | var index = allCandles.BinarySearchGetItem(i => allCandles[i].CloseTimeTicks, 0,
36 | p.DateTimeUtc.Ticks, BinarySearchMethod.PrevLowerValueOrValue);
37 | var currentCandles = allCandles
38 | .GetRange(index - CandlesCountForDataPoint + 1, CandlesCountForDataPoint).ToList();
39 |
40 | var x = GetMLPointXData(currentCandles);
41 | return (
42 | x,
43 | new List
44 | {
45 | p.PointType == MLPointType.Buy ? 1.0F : 0.0F, p.PointType == MLPointType.Sell ? 1.0F : 0.0F,
46 | p.PointType == MLPointType.Hold ? 1.0F : 0.0F
47 | },
48 | currentCandles);
49 | }
50 |
51 | public (List x, List y, int dataItemsPerX) GetPointsXYData(MLPointCollection points)
52 | {
53 | var candlesLookup = GetCandlesLookup(points);
54 | var rnd = new Random();
55 |
56 | var xValues = new List();
57 | var yValues = new List();
58 |
59 | var xpoints = 0;
60 | foreach (var p in points.Points)
61 | {
62 | var xy = GetPointXYData(p, candlesLookup);
63 | if (xpoints == 0) xpoints = xy.x.Count;
64 |
65 |
66 | xValues.AddRange(xy.x);
67 | yValues.AddRange(xy.y);
68 |
69 | if (points.GenerateExtraPoints)
70 | {
71 | for (var x = xy.candlesUsed[0].OpenBid * 0.001F;
72 | x < xy.candlesUsed[0].OpenBid * 0.003F;
73 | x += xy.candlesUsed[0].OpenBid * 0.0005F)
74 | {
75 | var gcandles = new List();
76 | var add = xy.candlesUsed[0].OpenBid * 0.001F;
77 | for (var i = 0; i < xy.candlesUsed.Count; i++)
78 | {
79 | var c = xy.candlesUsed[i];
80 | var gc = new Candle
81 | {
82 | OpenBid = c.OpenBid + add,
83 | HighBid = c.HighBid + add,
84 | CloseBid = c.CloseBid + add,
85 | LowBid = c.LowBid + add
86 | };
87 | gcandles.Add(gc);
88 |
89 | add = add + c.OpenBid * 0.001F;
90 | }
91 |
92 | xValues.AddRange(GetMLPointXData(gcandles));
93 | yValues.AddRange(new[]
94 | {
95 | p.PointType == MLPointType.Buy ? 1.0F : 0.0F,
96 | p.PointType == MLPointType.Sell ? 1.0F : 0.0F,
97 | p.PointType == MLPointType.Hold ? 1.0F : 0.0F
98 | });
99 | }
100 | }
101 | }
102 |
103 | return (xValues, yValues, xpoints);
104 | }
105 |
106 | public List GetMLPointXData(List allCandles, int uptoIndexInclusive = -1)
107 | {
108 | var candles =
109 | uptoIndexInclusive != -1
110 | ? allCandles.GetRange(uptoIndexInclusive - CandlesCountForDataPoint + 1, CandlesCountForDataPoint)
111 | : allCandles.GetRange(allCandles.Count - CandlesCountForDataPoint, CandlesCountForDataPoint);
112 | var maxPrice = candles.Select(x => x.HighBid).Max();
113 | var minPrice = candles.Select(x => x.LowBid).Min();
114 | var priceRange = maxPrice - minPrice;
115 |
116 | // Attempt 1: open prices, close prices
117 | /*var ret = new List();
118 | foreach (var c in candles)
119 | {
120 | ret.append((c.OpenBid - minPrice) / priceRange);
121 | ret.append((c.CloseBid - minPrice) / priceRange);
122 | ret.append((c.HighBid - minPrice) / priceRange);
123 | ret.append((c.LowBid - minPrice) / priceRange);
124 | }*/
125 |
126 | // Attempt 2: candle body sizes
127 | var ret = new List();
128 |
129 | // Up candle bodies
130 | foreach (var c in candles)
131 | {
132 | if (c.CloseBid > c.OpenBid)
133 | ret.Add((c.CloseBid - c.OpenBid) / priceRange);
134 | else
135 | {
136 | ret.Add(0.0F);
137 | }
138 | }
139 |
140 | // Down candle bodies
141 | foreach (var c in candles)
142 | {
143 | if (c.CloseBid < c.OpenBid)
144 | ret.Add((c.OpenBid - c.CloseBid) / priceRange);
145 | else
146 | {
147 | ret.Add(0.0F);
148 | }
149 | }
150 |
151 | if (ret.Any(zz => zz < 0F || zz > 1.0F)) throw new ApplicationException("Training data invalid");
152 |
153 | return ret;
154 | }
155 |
156 | private Dictionary<(string Market, Timeframe Tf), List> GetCandlesLookup(MLPointCollection p)
157 | {
158 | return GetCandlesLookup(
159 | p.Points.Select(x => (x.Market, x.Timeframe)).Distinct().ToList(),
160 | p.Broker,
161 | p.UseHeikenAshi);
162 | }
163 |
164 | private Dictionary<(string Market, Timeframe Tf), List> GetCandlesLookup(
165 | List<(string Market, Timeframe Timeframe)> marketsAndTimeframes,
166 | string broker,
167 | bool useHeikenAshi)
168 | {
169 | var lookup = new Dictionary<(string Market, Timeframe Tf), List>();
170 | foreach (var x in marketsAndTimeframes.Distinct())
171 | {
172 | var candles = _candlesService.GetCandles(
173 | _brokersService.GetBroker(broker),
174 | x.Market,
175 | x.Timeframe,
176 | false);
177 |
178 | if (useHeikenAshi) candles = candles.CreateHeikinAshiCandles();
179 |
180 | lookup[(x.Market, x.Timeframe)] = candles;
181 | }
182 |
183 | return lookup;
184 | }
185 | }
186 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/ML/TensorFlowTextWriter.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Reflection;
3 | using System.Text;
4 | using log4net;
5 |
6 | namespace StrategyEditor.ML
7 | {
8 | internal class TensorFlowTextWriter : TextWriter
9 | {
10 | private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
11 |
12 | public override void Write(string value)
13 | {
14 | Log.Info(value);
15 | }
16 |
17 | public override void WriteLine(string value)
18 | {
19 | Log.Info(value);
20 | }
21 |
22 | public override Encoding Encoding { get; }
23 | }
24 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/ML/Trainer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using System.Windows.Threading;
8 | using log4net;
9 | using Numpy;
10 | using StrategyEditor.ViewModels;
11 | using TraderTools.Basics;
12 |
13 | /* TODOusing Tensorflow;
14 | using static Tensorflow.KerasApi;
15 | using static Tensorflow.Binding;
16 | using Tensorflow.Keras;
17 | using Tensorflow.Keras.ArgsDefinition;
18 | using Tensorflow.Keras.Engine;
19 | using Tensorflow.Keras.Layers;
20 | using Tensorflow.NumPy;
21 | using TraderTools.Basics;*/
22 |
23 | namespace StrategyEditor.ML
24 | {
25 | public class MLFoundPoint
26 | {
27 | public MLFoundPoint(long dateTime, TradeDirection direction, decimal price)
28 | {
29 | DateTime = dateTime;
30 | Direction = direction;
31 | Price = price;
32 | }
33 |
34 | public long DateTime { get; }
35 | public TradeDirection Direction { get; }
36 | public decimal Price { get; }
37 | }
38 |
39 | public class Trainer
40 | {
41 | private IBrokersCandlesService _candlesService;
42 | private readonly IBrokersService _brokersService;
43 | // TODO private Sequential _model;
44 | private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
45 | private TaskScheduler _modelThreadContext;
46 | private Dispatcher _modelDispatcher;
47 | private DataGenerator _dataGenerator;
48 |
49 | static Trainer()
50 | {
51 | /* TODO Tensorflow.ops.enforce_singlethreading();
52 | Binding.tf_output_redirect = new TensorFlowTextWriter();*/
53 | }
54 |
55 | public Trainer(IBrokersCandlesService candlesService, IBrokersService brokersService)
56 | {
57 | _candlesService = candlesService;
58 | _brokersService = brokersService;
59 |
60 | _dataGenerator = new DataGenerator(_candlesService, _brokersService);
61 | ManualResetEvent dispatcherReadyEvent = new ManualResetEvent(false);
62 |
63 | new Thread(new ThreadStart(() =>
64 | {
65 | // if (!tf.executing_eagerly())
66 | // tf.enable_eager_execution();
67 | // tf.Context.ensure_initialized();
68 |
69 | _modelDispatcher = Dispatcher.CurrentDispatcher;
70 | dispatcherReadyEvent.Set();
71 | Dispatcher.Run();
72 | })).Start();
73 |
74 | dispatcherReadyEvent.WaitOne();
75 | }
76 |
77 | public Task> TestAsync(List candles)
78 | {
79 | return Task.Run(() =>
80 | {
81 | if (Dispatcher.CurrentDispatcher == _modelDispatcher)
82 | {
83 | return Test(candles);
84 | }
85 | else
86 | {
87 | List ret = null;
88 | _modelDispatcher.Invoke(() =>
89 | {
90 | ret = Test(candles);
91 | });
92 |
93 | return ret;
94 | }
95 | });
96 | }
97 |
98 | private List Test(List candles)
99 | {
100 | return null;
101 | /* TODO Log.Info("Running test");
102 | try
103 | {
104 | var ret = new List();
105 | var x = new List();
106 | var xpoints = 0;
107 | for (var i = 20; i < candles.Count; i++)
108 | {
109 | var z = _dataGenerator.GetMLPointXData(candles, i);
110 | if (xpoints == 0) xpoints = z.Count;
111 |
112 | x.AddRange(z);
113 | }
114 |
115 | var inX = np.array(x.ToArray());
116 | var countIn = (int)(x.Count / (decimal)xpoints);
117 |
118 | inX = inX.reshape(new int[] { countIn, xpoints });
119 |
120 | var outY = _model.predict(inX, countIn);
121 | for (var i = 0; i < countIn; i++)
122 | {
123 | var values = outY[0][i].ToArray();
124 | if (values.Any(v => v > 1F || v < 0F))
125 | {
126 | throw new ApplicationException("Test values is <0 or >1");
127 | }
128 |
129 | var buy = values[0];
130 | var sell = values[1];
131 |
132 | if (buy > 0.95F) ret.Add(new MLFoundPoint(candles[i].CloseTimeTicks, TradeDirection.Long, (decimal)candles[i].CloseBid));
133 | if (sell > 0.95F) ret.Add(new MLFoundPoint(candles[i].CloseTimeTicks, TradeDirection.Short, (decimal)candles[i].CloseBid));
134 | }
135 |
136 | Log.Info($"Test complete - {ret.Count} points found");
137 | return ret;
138 | }
139 | catch (Exception ex)
140 | {
141 | Log.Error("Failing to run test", ex);
142 | return null;
143 | }*/
144 | }
145 |
146 | public Task TrainAsync(MLPointCollection points)
147 | {
148 | return Task.Factory.StartNew(() =>
149 | {
150 | /* TODO _modelDispatcher.Invoke(() =>
151 | {
152 | tf.Context.ensure_initialized();
153 | Log.Info("Training");
154 | var xydata = _dataGenerator.GetPointsXYData(points);
155 |
156 | var x = np.array(xydata.x.ToArray());
157 | var y = np.array(xydata.y.ToArray());
158 | var count = (int)(xydata.x.Count / (decimal)xydata.dataItemsPerX);
159 | x = x.reshape(new int[] { count, xydata.dataItemsPerX });
160 | y = y.reshape(new int[] { count, 3 });
161 |
162 | //Tensorflow.InvalidArgumentError: 'In[0] mismatch In[1] shape: 28 vs. 1120: [5,28] [1120,60] 0 0'
163 | /*_model = keras.Sequential(
164 | new List
165 | {
166 | new Flatten(new FlattenArgs
167 | {
168 | InputShape = new Shape(new [] { xydata.dataItemsPerX })
169 | }),
170 | //keras.layers.Flatten(),
171 | keras.layers.Dense(xydata.dataItemsPerX, activation: "relu"),//, input_shape: new TensorShape(-1, xydata.dataItemsPerX)),
172 | keras.layers.Dense(60, activation: "relu"),
173 | keras.layers.Dense(40, activation: "relu"),
174 | keras.layers.Dense(3, activation: "softmax"),
175 | });
176 |
177 | _model.compile(keras.optimizers.SGD(0.01F), keras.losses.CategoricalCrossentropy(from_logits: true));*/
178 |
179 |
180 | /* CategoricalCrossentropy /
181 |
182 | var numberOfClasses = 3;
183 | _model = keras.Sequential(
184 | new List
185 | {
186 | new Flatten(new FlattenArgs
187 | {
188 | InputShape = new Shape(xydata.dataItemsPerX)
189 | }),
190 | //keras.layers.Flatten(),
191 | keras.layers.Dense(xydata.dataItemsPerX, activation: "relu"),//, input_shape: new TensorShape(-1, xydata.dataItemsPerX)),
192 | keras.layers.Dropout(0.2F),
193 | keras.layers.Dense(12, activation: "relu"),
194 | keras.layers.Dropout(0.2F),
195 | keras.layers.Dense(6, activation: "relu"),
196 | keras.layers.Dense(numberOfClasses, activation: "softmax"),
197 | });
198 |
199 | //var loss = new SGD(0.05F);
200 | //var optimiser = new SparseCategoricalCrossentropy();
201 | //model.compile(loss, optimiser, new[] { "accuracy" });
202 | //model.compile(new SGD(0.1F), new SparseCategoricalCrossentropy(), new[] { "accuracy" });
203 |
204 | // logits and labels must have the same first dimension, got logits shape [5,3] and labels shape [15]'
205 | _model.compile(
206 | keras.optimizers.Adam(0.01F),
207 | keras.losses.CategoricalCrossentropy(),
208 | new[] { "acc" });
209 |
210 | //here // SparseCategoricalCrossentropy? Validation set? More generated data?/
211 |
212 | _model.fit(x, y, 5, 100, 1, validation_split: 0.1F);
213 | Log.Info("Training complete");/
214 | });*/
215 | });//, TaskCreationOptions.LongRunning);
216 | }
217 | }
218 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.ComponentModel.Composition;
4 | using System.Reflection;
5 | using System.Windows;
6 | using StrategyEditor.ViewModels;
7 | using Hallupa.Library;
8 | using log4net;
9 | using TraderTools.Basics;
10 |
11 | namespace StrategyEditor
12 | {
13 | ///
14 | /// Interaction logic for MainWindow.xaml
15 | ///
16 | public partial class MainWindow : Window
17 | {
18 | private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
19 |
20 | [Import]
21 | private IBrokersService _brokersService;
22 |
23 | public MainWindow()
24 | {
25 | InitializeComponent();
26 |
27 | DependencyContainer.ComposeParts(this);
28 |
29 | DataContext = new MainWindowsViewModel();
30 | Closing += OnClosing;
31 | }
32 |
33 | private void OnClosing(object sender, CancelEventArgs cancelEventArgs)
34 | {
35 | ((IDisposable)_brokersService).Dispose();
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/Services/StrategyRunnerResultsService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.Composition;
4 | using System.Reactive.Linq;
5 | using System.Reactive.Subjects;
6 | using Hallupa.TraderTools.Basics;
7 | using Hallupa.TraderTools.Simulation;
8 | using TraderTools.Basics;
9 |
10 | namespace StrategyEditor.Services
11 | {
12 | [Export(typeof(StrategyRunnerResultsService))]
13 | [PartCreationPolicy(CreationPolicy.Shared)]
14 | public class StrategyRunnerResultsService
15 | {
16 | private StrategyBase _strategyBase;
17 | private Dictionary _initialAssetBalances;
18 | private Subject<(List Trades, StrategyBase Strategy)> _testResultsUpdated = new Subject<(List Trades, StrategyBase Strategy)>();
19 |
20 | public IObservable<(List Trades, StrategyBase Strategy)> TestResultsUpdated => _testResultsUpdated.AsObservable();
21 |
22 | private Subject<(List Trades, StrategyBase Strategy, Dictionary InitialAssetBalances)> _testRunCompleted = new Subject<(List Trades, StrategyBase Strategy, Dictionary InitialAssetBalances)>();
23 |
24 | private Subject<(List Trades, StrategyBase Strategy, Dictionary InitialAssetBalances)> _testRunStarted = new Subject<(List Trades, StrategyBase Strategy, Dictionary InitialAssetBalances)>();
25 |
26 | public IObservable<(List Trades, StrategyBase Strategy, Dictionary InitialAssetBalances)> TestRunCompleted =>
27 | _testRunCompleted.AsObservable();
28 |
29 | public IObservable<(List Trades, StrategyBase Strategy, Dictionary InitialAssetBalances)> TestRunStarted => _testRunStarted.AsObservable();
30 |
31 | public void AddResult(List result, StrategyBase strategyBase, Dictionary initialAssetBalances)
32 | {
33 | lock (Results)
34 | {
35 | Results.AddRange(result);
36 | }
37 |
38 | _strategyBase = strategyBase;
39 | _initialAssetBalances = initialAssetBalances;
40 | _testResultsUpdated.OnNext((result, _strategyBase));
41 | }
42 |
43 | public void RaiseTestRunCompleted()
44 | {
45 | _testRunCompleted.OnNext((Results, _strategyBase, _initialAssetBalances));
46 | }
47 |
48 | public void RaiseTestRunStarted()
49 | {
50 | _testRunStarted.OnNext((Results, _strategyBase, _initialAssetBalances));
51 | }
52 |
53 | public void Reset()
54 | {
55 | lock (Results)
56 | {
57 | Results.Clear();
58 | }
59 |
60 | _testResultsUpdated.OnNext((new List(), null));
61 | }
62 |
63 | public List Results { get; } = new List();
64 | }
65 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/Services/UIService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.ComponentModel.Composition;
5 | using System.Reactive.Linq;
6 | using System.Reactive.Subjects;
7 | using System.Runtime.CompilerServices;
8 | using System.Windows;
9 | using StrategyEditor.ViewModels;
10 | using Hallupa.Library;
11 |
12 | namespace StrategyEditor.Services
13 | {
14 | [Export(typeof(UIService))]
15 | [PartCreationPolicy(CreationPolicy.Shared)]
16 | public class UIService : DependencyObject, INotifyPropertyChanged
17 | {
18 | private DisplayPages _selectedDisplayPage = DisplayPages.RunStrategy;
19 | private Subject _viewTradeObservable;
20 | private Subject _viewTradeSetupObservable;
21 | private List _f5Actions = new List();
22 | private List _cntrlSActions = new List();
23 |
24 | public UIService()
25 | {
26 | ViewTradeCommand = new DelegateCommand(o => ViewTrade());
27 | }
28 |
29 | public DelegateCommand ViewTradeCommand { get; private set; }
30 |
31 | public string SelectedStrategyFilename { get; set; }
32 |
33 | public static readonly DependencyProperty IsViewTradeEnabledProperty = DependencyProperty.Register(
34 | "IsViewTradeEnabled", typeof(bool), typeof(UIService), new PropertyMetadata(true));
35 |
36 | public bool IsViewTradeEnabled
37 | {
38 | get { return (bool) GetValue(IsViewTradeEnabledProperty);
39 | }
40 | set { SetValue(IsViewTradeEnabledProperty, value);
41 | }
42 | }
43 |
44 | public static readonly DependencyProperty UseHeikenAshiProperty = DependencyProperty.Register(
45 | "UseHeikenAshi", typeof(bool), typeof(UIService), new PropertyMetadata(false, UseHeikenAshiChanged));
46 |
47 | private static void UseHeikenAshiChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
48 | {
49 | ((UIService)d).ViewTrade();
50 | }
51 |
52 | public bool UseHeikenAshi
53 | {
54 | get { return (bool) GetValue(UseHeikenAshiProperty); }
55 | set { SetValue(UseHeikenAshiProperty, value); }
56 | }
57 |
58 | public DisplayPages? SelectedDisplayPage
59 | {
60 | get => _selectedDisplayPage;
61 | set
62 | {
63 | if (_selectedDisplayPage == value || value == null)
64 | {
65 | return;
66 | }
67 |
68 | _selectedDisplayPage = value.Value;
69 | OnPropertyChanged();
70 | }
71 | }
72 |
73 | public void RegisterF5Action(Action action, bool placeFirst = false)
74 | {
75 | if (placeFirst)
76 | {
77 | _f5Actions.Insert(0, action);
78 | }
79 | else
80 | {
81 | _f5Actions.Add(action);
82 | }
83 | }
84 |
85 | public void RaiseF5Pressed()
86 | {
87 | foreach (var action in _f5Actions)
88 | {
89 | action();
90 | }
91 | }
92 |
93 | private Subject ViewTradeSubject => _viewTradeObservable ?? (_viewTradeObservable = new Subject());
94 |
95 | public IObservable ViewTradeObservable => ViewTradeSubject.AsObservable();
96 |
97 | public string SelectedCodeText { get; set; }
98 |
99 | private void ViewTrade()
100 | {
101 | ViewTradeSubject.OnNext(this);
102 | }
103 |
104 | public event PropertyChangedEventHandler PropertyChanged;
105 |
106 | protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
107 | {
108 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
109 | }
110 |
111 | public void RegisterControlSAction(Action action, bool placeFirst = false)
112 | {
113 | if (placeFirst)
114 | {
115 | _cntrlSActions.Insert(0, action);
116 | }
117 | else
118 | {
119 | _cntrlSActions.Add(action);
120 | }
121 | }
122 |
123 | public void RaiseControlSPressed()
124 | {
125 | foreach (var action in _cntrlSActions)
126 | {
127 | action();
128 | }
129 | }
130 | }
131 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace StrategyEditor {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 |
26 | [global::System.Configuration.UserScopedSettingAttribute()]
27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
28 | [global::System.Configuration.DefaultSettingValueAttribute("")]
29 | public string Username {
30 | get {
31 | return ((string)(this["Username"]));
32 | }
33 | set {
34 | this["Username"] = value;
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/StrategyEditor.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net6.0-windows
6 | true
7 | AnyCPU;x64
8 | StrategyEditor.App
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | PreserveNewest
36 |
37 |
38 | PreserveNewest
39 |
40 |
41 | True
42 | True
43 | Settings.settings
44 |
45 |
46 | Code
47 |
48 |
49 | Code
50 |
51 |
52 |
53 |
54 |
55 | PreserveNewest
56 |
57 |
58 | PreserveNewest
59 |
60 |
61 | SettingsSingleFileGenerator
62 | Settings.Designer.cs
63 |
64 |
65 |
66 |
67 |
68 | Designer
69 |
70 |
71 | Designer
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/ViewModels/CreatePointViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.ObjectModel;
3 | using Hallupa.Library;
4 |
5 | namespace StrategyEditor.ViewModels
6 | {
7 | public class CreatePointViewModel
8 | {
9 | private readonly Action _closeAction;
10 |
11 | public CreatePointViewModel(Action closeAction)
12 | {
13 | _closeAction = closeAction;
14 | Options.Add(MLPointType.Buy);
15 | Options.Add(MLPointType.Sell);
16 | Options.Add(MLPointType.Hold);
17 | OkCommand = new DelegateCommand(o =>
18 | {
19 | Ok = true;
20 | _closeAction();
21 | });
22 | CancelCommand = new DelegateCommand(o =>
23 | {
24 | _closeAction();
25 | });
26 | }
27 |
28 | public ObservableCollection Options { get; private set; } = new ObservableCollection();
29 | public MLPointType SelectedOption { get; set; } = MLPointType.Buy;
30 | public DelegateCommand OkCommand { get; private set; }
31 | public DelegateCommand CancelCommand { get; private set; }
32 | public bool Ok { get; private set; } = false;
33 | }
34 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/ViewModels/LoginOutViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.ComponentModel.Composition;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Runtime.CompilerServices;
7 | using System.Threading.Tasks;
8 | using System.Windows;
9 | using System.Windows.Threading;
10 | using Hallupa.Library;
11 | using log4net;
12 | using StrategyEditor.Views;
13 | using TraderTools.Basics;
14 | using TraderTools.Brokers.FXCM;
15 |
16 | namespace StrategyEditor.ViewModels
17 | {
18 | public class LoginOutViewModel : INotifyPropertyChanged
19 | {
20 | private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
21 |
22 | private string _loginOutButtonText;
23 | private bool _loginOutButtonEnabled = true;
24 | private readonly FxcmBroker _fxcm;
25 | private Action> _createLoginViewFunc;
26 | private Func<(Action show, Action updateText, Action close)> _createProgressingViewFunc;
27 | private Dispatcher _dispatcher;
28 |
29 | [Import] private IBrokersService _brokersService;
30 | [Import] private IMarketDetailsService _marketsService;
31 |
32 | public LoginOutViewModel()
33 | {
34 | DependencyContainer.ComposeParts(this);
35 |
36 | LoginOutCommand = new DelegateCommand(o => LoginOut(), o => LoginOutButtonEnabled);
37 | _loginOutButtonText = "Login";
38 | _fxcm = (FxcmBroker)_brokersService.Brokers.First(x => x.Name == "FXCM");
39 | _dispatcher = Dispatcher.CurrentDispatcher;
40 |
41 | _createLoginViewFunc = loginAction =>
42 | {
43 | var view = new LoginView { Owner = Application.Current.MainWindow };
44 | var loginVm = new LoginViewModel(() => view.Close(), loginAction);
45 | view.DataContext = loginVm;
46 | view.ShowDialog();
47 | };
48 |
49 | _createProgressingViewFunc = () =>
50 | {
51 | var view = new ProgressView { Owner = Application.Current.MainWindow };
52 |
53 | return (text =>
54 | {
55 | view.TextToShow.Text = text;
56 | view.ShowDialog();
57 | },
58 | txt =>
59 | {
60 | if (_dispatcher.CheckAccess())
61 | {
62 | view.TextToShow.Text = txt;
63 | }
64 | else
65 | {
66 | _dispatcher.BeginInvoke((Action)(() => { view.TextToShow.Text = txt; }));
67 | }
68 | },
69 | () => view.Close());
70 | };
71 | }
72 |
73 | public string LoginOutButtonText
74 | {
75 | get => _loginOutButtonText;
76 | set
77 | {
78 | _loginOutButtonText = value;
79 | OnPropertyChanged();
80 | }
81 | }
82 |
83 | public bool LoginOutButtonEnabled
84 | {
85 | get => _loginOutButtonEnabled;
86 | set
87 | {
88 | _loginOutButtonEnabled = value;
89 | LoginOutCommand.RaiseCanExecuteChanged();
90 | OnPropertyChanged();
91 | }
92 | }
93 |
94 | public DelegateCommand LoginOutCommand { get; }
95 |
96 |
97 | public event PropertyChangedEventHandler PropertyChanged;
98 |
99 | protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
100 | {
101 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
102 | }
103 |
104 | private void LoginOut()
105 | {
106 | LoginOutButtonEnabled = false;
107 | var loginAttempted = false;
108 |
109 | if (_fxcm.Status != ConnectStatus.Connected)
110 | {
111 | var progressViewActions = _createProgressingViewFunc();
112 |
113 | _createLoginViewFunc((username, password, connection) =>
114 | {
115 | Task.Run(() =>
116 | {
117 | try
118 | {
119 | loginAttempted = true;
120 | _fxcm.SetUsernamePassword(username, password, connection);
121 | _fxcm.Connect();
122 |
123 | if (_fxcm.Status == ConnectStatus.Connected)
124 | {
125 | var marketDetailsList = _fxcm.GetMarketDetailsList();
126 | if (marketDetailsList != null)
127 | {
128 | foreach (var marketDetails in marketDetailsList)
129 | {
130 | _marketsService.AddMarketDetails(marketDetails);
131 | }
132 |
133 | _marketsService.SaveMarketDetailsList();
134 | }
135 | }
136 | }
137 | catch (Exception ex)
138 | {
139 | Log.Error("Unable to login", ex);
140 | }
141 |
142 | _dispatcher.Invoke(() =>
143 | {
144 | progressViewActions.close();
145 | });
146 | });
147 |
148 | progressViewActions.show("Logging in...");
149 | });
150 | }
151 | else
152 | {
153 | var progressViewActions = _createProgressingViewFunc();
154 |
155 | Task.Run(() =>
156 | {
157 | _fxcm.Disconnect();
158 |
159 | _dispatcher.Invoke(() =>
160 | {
161 | progressViewActions.close();
162 | LoginOutButtonEnabled = true;
163 | UpdateLoginButtonText();
164 | });
165 | });
166 |
167 | progressViewActions.show("Logging out...");
168 | }
169 |
170 | LoginOutButtonEnabled = true;
171 |
172 | UpdateLoginButtonText();
173 | if (_fxcm.Status != ConnectStatus.Connected && loginAttempted)
174 | {
175 | MessageBox.Show("Unable to login", "Failed", MessageBoxButton.OK);
176 | }
177 | }
178 |
179 | private void UpdateLoginButtonText()
180 | {
181 | if (_fxcm.Status == ConnectStatus.Connected)
182 | {
183 | LoginOutButtonText = "Logout";
184 | }
185 | else
186 | {
187 | LoginOutButtonText = "Login";
188 | }
189 | }
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/ViewModels/LoginViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Controls;
3 | using Hallupa.Library;
4 |
5 | namespace StrategyEditor.ViewModels
6 | {
7 | public class LoginViewModel
8 | {
9 | private readonly Action _closeViewAction;
10 | private readonly Action _loginAction;
11 |
12 | public LoginViewModel(Action closeViewAction, Action loginAction)
13 | {
14 | Username = Settings.Default.Username;
15 |
16 | _closeViewAction = closeViewAction;
17 | _loginAction = loginAction;
18 | LoginCommand = new DelegateCommand(Login);
19 | }
20 |
21 | public DelegateCommand LoginCommand { get; }
22 |
23 | public string Username { get; set; }
24 |
25 | public string Connection { get; set; } = "GBREAL";
26 |
27 | private void Login(object obj)
28 | {
29 | var passwordBox = (PasswordBox)obj;
30 | var password = passwordBox.Password;
31 | _closeViewAction();
32 |
33 | Settings.Default.Username = Username;
34 | Settings.Default.Save();
35 |
36 | _loginAction(Username, password, Connection);
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/ViewModels/MachineLearningChartViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.Composition;
4 | using System.Linq;
5 | using System.Windows.Threading;
6 | using Hallupa.Library;
7 | using Hallupa.TraderTools.Brokers.Binance;
8 | using SciChart.Charting.Model.DataSeries;
9 | using TraderTools.Basics;
10 | using TraderTools.Basics.Extensions;
11 | using TraderTools.Core.Extensions;
12 | using TraderTools.Core.UI;
13 |
14 | namespace StrategyEditor.ViewModels
15 | {
16 | public class MachineLearningChartViewModel
17 | {
18 | [Import] private IBrokersService _brokersService;
19 | [Import] private IBrokersCandlesService _candlesService;
20 | private Timeframe _selectedTimeframe;
21 | private bool _useHeikenAshi;
22 | private BinanceBroker _broker;
23 |
24 | public MachineLearningChartViewModel()
25 | {
26 | DependencyContainer.ComposeParts(this);
27 |
28 | _broker = (BinanceBroker) _brokersService.Brokers.First(b => b.Name == "Binance");
29 |
30 | ChartViewModel = new ChartViewModel();
31 | ViewMarketCommand = new DelegateCommand(o => ViewMarket());
32 | TimeframeOptions = new List();
33 | TimeframeOptions.AddRange(new[] {Timeframe.M15, Timeframe.H1, Timeframe.H2, Timeframe.H4, Timeframe.D1});
34 | SelectedTimeframe = Timeframe.H1;
35 | Market = "BTCUSDT";
36 | }
37 |
38 | public ChartViewModel ChartViewModel { get; private set; }
39 | public DelegateCommand ViewMarketCommand { get; }
40 | public string Market { get; set; }
41 | public List TimeframeOptions { get; }
42 |
43 | public bool UseHeikenAshi
44 | {
45 | get => _useHeikenAshi;
46 | set
47 | {
48 | _useHeikenAshi = value;
49 | ViewMarket();
50 | }
51 | }
52 |
53 | public Timeframe SelectedTimeframe
54 | {
55 | get => _selectedTimeframe;
56 | set
57 | {
58 | _selectedTimeframe = value;
59 | ViewMarket();
60 | }
61 | }
62 |
63 | private void ViewMarket()
64 | {
65 | if (string.IsNullOrEmpty(Market)) return;
66 |
67 | var candles = _candlesService.GetDerivedCandles(_broker, Market, SelectedTimeframe);
68 |
69 | if (UseHeikenAshi) candles = candles.CreateHeikinAshiCandles();
70 |
71 | var priceDataSeries = new OhlcDataSeries();
72 | var xvalues = new List();
73 | var openValues = new List();
74 | var highValues = new List();
75 | var lowValues = new List();
76 | var closeValues = new List();
77 |
78 | for (var i = 0; i < candles.Count; i++)
79 | {
80 | var time = new DateTime(candles[i].CloseTimeTicks);
81 |
82 | xvalues.Add(time);
83 | openValues.Add((double) candles[i].OpenBid);
84 | highValues.Add((double) candles[i].HighBid);
85 | lowValues.Add((double) candles[i].LowBid);
86 | closeValues.Add((double) candles[i].CloseBid);
87 | }
88 |
89 | priceDataSeries.Append(xvalues, openValues, highValues, lowValues, closeValues);
90 | priceDataSeries.SeriesName = "Price";
91 |
92 | //ChartViewModel.ChartPaneViewModels.Clear();
93 | ChartHelper.SetChartViewModelPriceData(candles, ChartViewModel);
94 |
95 | /*var pricePaneVm = new ChartPaneViewModel(ChartViewModel, ChartViewModel.ViewportManager)
96 | {
97 | IsFirstChartPane = false,
98 | IsLastChartPane = false,
99 | Height = 400
100 | };
101 | ChartViewModel.ChartPaneViewModels.Add(pricePaneVm);*/
102 |
103 | // ChartHelper.SetChartViewModelPriceData(candles, ChartViewModel);
104 |
105 | /*var indicatorChartPaneViewModel =
106 | new ChartPaneViewModel(ChartViewModel, ChartViewModel.ViewportManager)
107 | {
108 | IsFirstChartPane = false,
109 | IsLastChartPane = true,
110 | Height = 200
111 | };
112 |
113 | ChartHelper.AddIndicator(
114 | indicatorChartPaneViewModel,
115 | ViewPairText,
116 | new StochasticRelativeStrengthIndex(),
117 | Colors.Blue,
118 | timeframe,
119 | candles);
120 | ChartViewModel.ChartPaneViewModels.Add(indicatorChartPaneViewModel);*/
121 | }
122 | }
123 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/ViewModels/MachineLearningViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.ComponentModel.Composition;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Reflection;
8 | using System.Threading.Tasks;
9 | using System.Windows;
10 | using System.Windows.Threading;
11 | using Hallupa.Library;
12 | using log4net;
13 | using Newtonsoft.Json;
14 | using SciChart.Charting.Visuals.Annotations;
15 | using StrategyEditor.ML;
16 | using StrategyEditor.Views;
17 | using TraderTools.Basics;
18 | using TraderTools.Basics.Extensions;
19 | using TraderTools.Core.Extensions;
20 | using TraderTools.Core.UI;
21 | using TraderTools.Core.UI.Services;
22 |
23 | namespace StrategyEditor.ViewModels
24 | {
25 | public class MachineLearningViewModel : DependencyObject
26 | {
27 | [Import] private IDataDirectoryService _dataDirectoryService;
28 | [Import] private IBrokersCandlesService _candlesService;
29 | [Import] private IBrokersService _brokersService;
30 | private IDisposable _chartClickedDisposable;
31 | private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
32 |
33 | public MachineLearningViewModel()
34 | {
35 | _dispatcher = Dispatcher;
36 | DependencyContainer.ComposeParts(this);
37 | CreatePointCommand = new DelegateCommand(o => CreatePoint());
38 | ViewPointCommand = new DelegateCommand(o => ViewPoint());
39 | CreatePointSetCommand = new DelegateCommand(o => CreatePointSet());
40 | DeletePointSetCommand = new DelegateCommand(o => DeletePointSet());
41 | DeletePointCommand = new DelegateCommand(o => DeletePoint());
42 | TrainCommand = new DelegateCommand(o => Train(), o => IsTrainingEnabled);
43 | TestCommand = new DelegateCommand(o => TestModel(), o => IsTestEnabled);
44 | MLPointDoubleClickComamnd = new DelegateCommand(o => MLPointDoubleClicked());
45 | Chart = new MachineLearningChartViewModel();
46 | _chartClickedDisposable = ChartingService.ChartClickObservable.Subscribe(ChartClicked);
47 | Load();
48 | }
49 |
50 | public DelegateCommand MLPointDoubleClickComamnd { get; }
51 |
52 | public DelegateCommand ViewPointCommand { get; }
53 |
54 | public DelegateCommand DeletePointCommand { get; }
55 |
56 | public DelegateCommand TestCommand { get; }
57 |
58 | public DelegateCommand TrainCommand { get; }
59 |
60 | public static readonly DependencyProperty CreatingPointProperty = DependencyProperty.Register(
61 | "CreatingPoint", typeof(bool?), typeof(MachineLearningViewModel), new PropertyMetadata(false));
62 |
63 | public bool? CreatingPoint
64 | {
65 | get { return (bool?)GetValue(CreatingPointProperty); }
66 | set { SetValue(CreatingPointProperty, value); }
67 | }
68 |
69 | [Import] public ChartingService ChartingService { get; set; }
70 |
71 | public MachineLearningChartViewModel Chart { get; }
72 | public bool IsTrainingEnabled { get; set; } = true;
73 | public bool IsTestEnabled { get; set; } = true;
74 | public ObservableCollection MLPointSets { get; } = new ObservableCollection();
75 |
76 | public static readonly DependencyProperty SelectedMLPointsSetProperty = DependencyProperty.Register(
77 | "SelectedMLPointsSet", typeof(MLPointCollection), typeof(MachineLearningViewModel), new PropertyMetadata(default(MLPointCollection)));
78 |
79 | private MLPoint _pointCreating;
80 | private Trainer _trainer;
81 | private Dispatcher _dispatcher;
82 |
83 | public MLPointCollection SelectedMLPointsSet
84 | {
85 | get { return (MLPointCollection)GetValue(SelectedMLPointsSetProperty); }
86 | set { SetValue(SelectedMLPointsSetProperty, value); }
87 | }
88 |
89 | public static readonly DependencyProperty SelectedMLPointProperty = DependencyProperty.Register(
90 | "SelectedMLPoint", typeof(MLPoint), typeof(MachineLearningViewModel), new PropertyMetadata(default(MLPoint)));
91 |
92 | public MLPoint SelectedMLPoint
93 | {
94 | get { return (MLPoint) GetValue(SelectedMLPointProperty); }
95 | set { SetValue(SelectedMLPointProperty, value); }
96 | }
97 |
98 | public DelegateCommand CreatePointCommand { get; }
99 | public DelegateCommand CreatePointSetCommand { get; }
100 | public DelegateCommand DeletePointSetCommand { get; }
101 |
102 | private void MLPointDoubleClicked()
103 | {
104 | ViewPoint();
105 | }
106 |
107 | private void Train()
108 | {
109 | if (SelectedMLPointsSet == null) return;
110 |
111 | IsTrainingEnabled = false;
112 | TrainCommand.RaiseCanExecuteChanged();
113 | _trainer = new Trainer(_candlesService, _brokersService);
114 | var pointsSet = SelectedMLPointsSet;
115 |
116 | _trainer.TrainAsync(pointsSet)
117 | .ContinueWith(o =>
118 | {
119 | _dispatcher.Invoke(() =>
120 | {
121 | IsTrainingEnabled = true;
122 | TrainCommand.RaiseCanExecuteChanged();
123 | });
124 | });
125 | }
126 |
127 | private void ChartClicked((DateTime Time, double Price, Action setIsHandled) o)
128 | {
129 | if (CreatingPoint == true && _pointCreating != null)
130 | {
131 | _pointCreating.DateTimeUtc = o.Time;
132 | _pointCreating.Market = Chart.Market;
133 | _pointCreating.Timeframe = Chart.SelectedTimeframe;
134 |
135 | SelectedMLPointsSet.Points.Add(_pointCreating);
136 | Save();
137 | _pointCreating = null;
138 | CreatingPoint = false;
139 | }
140 | }
141 |
142 | private void ViewPoint()
143 | {
144 | if (SelectedMLPoint == null || SelectedMLPointsSet == null) return;
145 |
146 | var dataGenerator = new DataGenerator(_candlesService, _brokersService);
147 | var xy = dataGenerator.GetPointXYData(SelectedMLPoint, SelectedMLPointsSet);
148 |
149 | Log.Info($"Point X data: {string.Join(", ", xy.x.Select(v => $"{v:0.00}"))}");
150 |
151 | Chart.ChartViewModel.ChartPaneViewModels.Clear();
152 | ChartHelper.SetChartViewModelPriceData(xy.candlesUsed, Chart.ChartViewModel, "ML point");
153 | }
154 |
155 | private void TestModel()
156 | {
157 | if (_trainer == null) return;
158 |
159 | IsTestEnabled = false;
160 | TestCommand.RaiseCanExecuteChanged();
161 |
162 | var candles = _candlesService.GetDerivedCandles(
163 | _brokersService.GetBroker(SelectedMLPointsSet.Broker),
164 | Chart.Market,
165 | Chart.SelectedTimeframe);
166 |
167 | if (SelectedMLPointsSet.UseHeikenAshi) candles = candles.CreateHeikinAshiCandles();
168 |
169 | var w = _trainer.TestAsync(candles)
170 | .ContinueWith(o =>
171 | {
172 | var results = o.Result;
173 | _dispatcher.Invoke(() =>
174 | {
175 | IsTestEnabled = true;
176 | TestCommand.RaiseCanExecuteChanged();
177 |
178 | if (Chart.ChartViewModel.ChartPaneViewModels.Count == 0) return;
179 |
180 | if (Chart.ChartViewModel.ChartPaneViewModels[0].TradeAnnotations == null)
181 | {
182 | Chart.ChartViewModel.ChartPaneViewModels[0].TradeAnnotations = new AnnotationCollection();
183 | }
184 |
185 | var annotations = new AnnotationCollection();
186 | foreach (var r in results)
187 | {
188 | ChartHelper.AddBuySellMarker(
189 | r.Direction, annotations, null,
190 | new DateTime(r.DateTime, DateTimeKind.Utc), r.Price,
191 | true, true);
192 | }
193 |
194 | Chart.ChartViewModel.ChartPaneViewModels[0].TradeAnnotations = annotations;
195 | });
196 | });
197 | }
198 |
199 | private void CreatePoint()
200 | {
201 | if (SelectedMLPointsSet == null || CreatingPoint == false) return;
202 |
203 | var view = new CreatePointView();
204 | view.ShowDialog();
205 |
206 | if (!view.Model.Ok) return;
207 |
208 | _pointCreating = new MLPoint
209 | {
210 | PointType = view.Model.SelectedOption
211 | };
212 | }
213 |
214 | private void Save()
215 | {
216 | File.WriteAllText(
217 | Path.Combine(
218 | _dataDirectoryService.MainDirectoryWithApplicationName, "MLPoints.json"),
219 | JsonConvert.SerializeObject(MLPointSets.ToList()));
220 | }
221 |
222 | private void Load()
223 | {
224 | var path = Path.Combine(_dataDirectoryService.MainDirectoryWithApplicationName, "MLPoints.json");
225 | if (!File.Exists(path)) return;
226 | var json = File.ReadAllText(path);
227 | var d = JsonConvert.DeserializeObject>(json);
228 |
229 | foreach (var z in d)
230 | {
231 | MLPointSets.Add(z);
232 | }
233 |
234 | SelectedMLPointsSet = MLPointSets.FirstOrDefault();
235 | }
236 |
237 | private void CreatePointSet()
238 | {
239 | var m = new MLPointCollection
240 | {
241 | Broker = "Binance"
242 | };
243 |
244 | var view = new CreatePointGroupView { DataContext = m };
245 | view.ShowDialog();
246 |
247 | if (!view.OKClicked) return;
248 |
249 | MLPointSets.Add(m);
250 | SelectedMLPointsSet = m;
251 | Save();
252 | }
253 |
254 | private void DeletePointSet()
255 | {
256 | if (SelectedMLPointsSet == null) return;
257 |
258 | MLPointSets.Remove(SelectedMLPointsSet);
259 | SelectedMLPointsSet = MLPointSets.FirstOrDefault();
260 | Save();
261 | }
262 |
263 | private void DeletePoint()
264 | {
265 | if (SelectedMLPointsSet == null || SelectedMLPoint == null) return;
266 |
267 | SelectedMLPointsSet.Points.Remove(SelectedMLPoint);
268 | Save();
269 | }
270 | }
271 |
272 | public class MLPointCollection
273 | {
274 | public string Name { get; set; }
275 | public ObservableCollection Points { get; set; } = new ObservableCollection();
276 | public bool UseHeikenAshi { get; set; }
277 | public string Broker { get; set; }
278 | public bool GenerateExtraPoints { get; set; } = true;
279 | }
280 |
281 | public class MLPoint
282 | {
283 | public DateTime DateTimeUtc { get; set; }
284 | public string Market { get; set; }
285 | public MLPointType PointType { get; set; }
286 | public Timeframe Timeframe { get; set; }
287 | }
288 |
289 | public enum MLPointType
290 | {
291 | Buy,
292 | Sell,
293 | Hold
294 | }
295 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/ViewModels/MainWindowsViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.ComponentModel.Composition;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Runtime.CompilerServices;
7 | using System.Threading.Tasks;
8 | using System.Windows;
9 | using System.Windows.Input;
10 | using CryptoTrader;
11 | using StrategyEditor.Services;
12 | using Hallupa.Library;
13 | using Hallupa.TraderTools.Brokers.Binance;
14 | using log4net;
15 | using StrategyEditor.Views;
16 | using TraderTools.Basics;
17 | using TraderTools.Basics.Helpers;
18 | using TraderTools.Brokers.FXCM;
19 | using TraderTools.Core.Services;
20 | using TraderTools.Core.UI.Services;
21 | using TraderTools.Simulation;
22 | using Dispatcher = System.Windows.Threading.Dispatcher;
23 |
24 | namespace StrategyEditor.ViewModels
25 | {
26 | public enum DisplayPages
27 | {
28 | RunStrategy,
29 | StrategyViewTrade,
30 | StrategyEquity,
31 | StrategyViewAllTrades,
32 | MachineLearning
33 | }
34 |
35 | public class MainWindowsViewModel : INotifyPropertyChanged
36 | {
37 | #region Fields
38 | private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
39 |
40 | [Import] private IBrokersService _brokersService;
41 | [Import] private IBrokersCandlesService _candleService;
42 | [Import] private MarketsService _marketsService;
43 | [Import] private IMarketDetailsService _marketDetailsService;
44 | [Import] private UIService _uiService;
45 | [Import] public UIService UIService { get; private set; }
46 | private bool _updatingCandles;
47 | private Dispatcher _dispatcher;
48 | private bool _includeCrypto = true;
49 |
50 | #endregion
51 |
52 | #region Constructors
53 | public MainWindowsViewModel()
54 | {
55 | DependencyContainer.ComposeParts(this);
56 |
57 | _dispatcher = Dispatcher.CurrentDispatcher;
58 |
59 | UpdateFXCandlesCommand = new DelegateCommand(UpdateFXCandles);
60 |
61 | var brokers = new IBroker[]
62 | {
63 | new FxcmBroker(),
64 | new BinanceBroker("", ""),
65 | };
66 |
67 | // Setup brokers and load accounts
68 | _brokersService.AddBrokers(brokers);
69 |
70 | EventManager.RegisterClassHandler(typeof(Window), Keyboard.KeyDownEvent, new KeyEventHandler(UIElement_OnPreviewKeyDown), true);
71 | LoginOutViewModel = new LoginOutViewModel();
72 | }
73 |
74 | public LoginOutViewModel LoginOutViewModel { get; private set; }
75 |
76 | private void UIElement_OnPreviewKeyDown(object sender, KeyEventArgs e)
77 | {
78 | if (e.Key == Key.F5)
79 | {
80 | _uiService.RaiseF5Pressed();
81 | }
82 | else if (e.Key == Key.S && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
83 | {
84 | _uiService.RaiseControlSPressed();
85 | }
86 | }
87 |
88 | private void UpdateFXCandles(object obj)
89 | {
90 | if (_updatingCandles) return;
91 | var dispatcher = Dispatcher.CurrentDispatcher;
92 | _updatingCandles = true;
93 |
94 | if (_includeCrypto)
95 | {
96 | var candleUpdater = new BinanceCandlesUpdater(
97 | (BinanceBroker)_brokersService.Brokers.First(b => b.Name == "Binance"),
98 | _candleService,
99 | _marketDetailsService);
100 |
101 | candleUpdater
102 | .RunAsync()
103 | .ContinueWith(
104 | t => Task.Run(() => { dispatcher.Invoke(() => { _updatingCandles = false; }); }));
105 | }
106 | else
107 | {
108 |
109 | }
110 | }
111 |
112 | #endregion
113 |
114 | #region Properties
115 | [Import]
116 | public ChartingService ChartingService { get; private set; }
117 |
118 | public DelegateCommand UpdateFXCandlesCommand { get; private set; }
119 |
120 | #endregion
121 |
122 | public event PropertyChangedEventHandler PropertyChanged;
123 |
124 | protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
125 | {
126 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
127 | }
128 | }
129 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/ViewModels/StrategyRunResultsChartViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.ComponentModel.Composition;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 | using System.Windows.Threading;
9 | using Hallupa.Library;
10 | using Hallupa.Library.Extensions;
11 | using Hallupa.TraderTools.Basics;
12 | using Hallupa.TraderTools.Simulation;
13 | using SciChart.Charting.Model.DataSeries;
14 | using SciChart.Charting.Visuals.RenderableSeries;
15 | using StrategyEditor.Services;
16 | using TraderTools.Basics;
17 | using TraderTools.Basics.Extensions;
18 |
19 | namespace StrategyEditor.ViewModels
20 | {
21 | public class StrategyRunResultsChartViewModel : DependencyObject
22 | {
23 | [Import] private StrategyRunnerResultsService _results;
24 | [Import] private IBrokersCandlesService _brokersCandlesService;
25 | [Import] private IBrokersService _brokerService;
26 | private IDisposable _testResultsStartedObserver;
27 | private IDisposable _testResultsUpdatedObserver;
28 | private Dispatcher _dispatcher;
29 |
30 | public StrategyRunResultsChartViewModel()
31 | {
32 | _dispatcher = Dispatcher.CurrentDispatcher;
33 | DependencyContainer.ComposeParts(this);
34 |
35 | SeriesList = new ObservableCollection();
36 | _testResultsStartedObserver = _results.TestRunStarted.Subscribe(UpdateChartData);
37 | _testResultsUpdatedObserver = _results.TestRunCompleted.Subscribe(UpdateChartData);
38 | ClearResultsChartCommand = new DelegateCommand(o => ClearResultsChart());
39 | }
40 |
41 | public static readonly DependencyProperty SeriesListProperty = DependencyProperty.Register(
42 | "SeriesList", typeof(ObservableCollection), typeof(StrategyRunResultsChartViewModel), new PropertyMetadata(default(ObservableCollection)));
43 |
44 | public ObservableCollection SeriesList
45 | {
46 | get { return (ObservableCollection)GetValue(SeriesListProperty); }
47 | set { SetValue(SeriesListProperty, value); }
48 | }
49 |
50 | public DelegateCommand ClearResultsChartCommand { get; private set; }
51 |
52 | private void ClearResultsChart()
53 | {
54 | SeriesList.Clear();
55 | }
56 |
57 | private void UpdateChartData((List Trades, StrategyBase Strategy, Dictionary InitialAssetBalances) d)
58 | {
59 | var series = new XyDataSeries();
60 |
61 | Task.Run(() =>
62 | {
63 | var xvalues = new List();
64 | var yvalues = new List();
65 |
66 | var startedTrades = d.Trades.Where(t => t.EntryDateTime != null && !t.Ignore).ToList();
67 |
68 | if (d.Strategy != null)
69 | {
70 | if (d.Strategy.BrokerKind == BrokerKind.SpreadBet)
71 | {
72 | var earliest = startedTrades.OrderBy(t => t.EntryDateTime).First().EntryDateTime.Value.Date;
73 | var latest = DateTime.UtcNow;
74 | var broker = _brokerService.GetBroker("FXCM");
75 |
76 | for (var date = earliest; date <= latest; date = date.AddDays(1))
77 | {
78 | var balance = 10000M;
79 | var currentTrades = d.Trades.Where(t => t.EntryDateTime <= date).ToList();
80 | foreach (var t in currentTrades)
81 |
82 | {
83 | if (date >= t.CloseDateTime)
84 | {
85 | balance += (decimal)t.Profit.Value;
86 | }
87 | else
88 | {
89 | var risk = t.RiskAmount.Value;
90 | var candle =
91 | _brokersCandlesService.GetLastClosedCandle(t.Market,
92 | _brokerService.GetBroker(t.Broker), Timeframe.D1, date,
93 | false);
94 | var price = (decimal)(t.TradeDirection == TradeDirection.Long
95 | ? candle.Value.CloseBid
96 | : candle.Value.CloseAsk);
97 |
98 |
99 | /*var stopDist = t.InitialStop.Value - t.EntryPrice;
100 | var profit = (((decimal)price - t.EntryPrice.Value) / stopDist) * risk;*/
101 | var profit = price * t.EntryQuantity.Value -
102 | t.EntryPrice.Value * t.EntryQuantity.Value; //TODO Add commission
103 | balance += (decimal)profit;
104 | }
105 |
106 | xvalues.Add(date);
107 | yvalues.Add((double)balance);
108 | }
109 | }
110 | }
111 | else
112 | {
113 |
114 |
115 | if (startedTrades.Count > 0)
116 | {
117 | var candlesLookup = new Dictionary<(string Market, IBroker Broker), List>();
118 | var broker = _brokerService.GetBroker(d.Strategy.Broker);
119 | var earliest = startedTrades.OrderBy(t => t.EntryDateTime).First().EntryDateTime.Value.Date;
120 | var latest = DateTime.UtcNow;
121 |
122 | var assetBalances = d.InitialAssetBalances.ToDictionary(x => x.Key,
123 | x => new AssetBalance(x.Key, x.Value.Balance));
124 | var orderedTrades = d.Trades.Where(t => t.EntryDateTime <= latest)
125 | .OrderBy(x => x.EntryDateTime)
126 | .ToList();
127 | var tradeIndex = 0;
128 |
129 | for (var date = earliest; date <= latest; date = date.AddDays(1))
130 | {
131 | while (tradeIndex < orderedTrades.Count &&
132 | orderedTrades[tradeIndex].EntryDateTime <= date)
133 | {
134 | var t = orderedTrades[tradeIndex];
135 | tradeIndex++;
136 |
137 | assetBalances.UpdateAssetBalance(t);
138 | }
139 |
140 | var valueUsd = GetAssetBalancesUsdtValue(date, candlesLookup, assetBalances, broker);
141 | xvalues.Add(date);
142 | yvalues.Add((double)valueUsd);
143 |
144 | }
145 |
146 |
147 |
148 | series.Append(xvalues, yvalues);
149 | }
150 | }
151 | }
152 |
153 | _dispatcher.Invoke(() =>
154 | {
155 | var renderableSeries = new FastLineRenderableSeries
156 | {
157 | DataSeries = series,
158 | StrokeThickness = 2
159 | };
160 |
161 | SeriesList.Add(renderableSeries);
162 | });
163 | });
164 | }
165 |
166 | private decimal GetAssetBalancesUsdtValue(
167 | DateTime currentDateTimeUtc, Dictionary<(string Market, IBroker Broker), List> candlesLookup,
168 | Dictionary assetBalances, IBroker broker)
169 | {
170 | var totalUsdtValue = 0M;
171 | foreach (var assetBalance in assetBalances)
172 | {
173 | if (assetBalance.Value.Asset == "USDT")
174 | {
175 | totalUsdtValue += assetBalance.Value.Balance;
176 | continue;
177 | }
178 |
179 | var usdtMarket = $"{assetBalance.Key}USDT";
180 | if (!candlesLookup.ContainsKey((usdtMarket, broker)))
181 | {
182 | candlesLookup[(usdtMarket, broker)] = _brokersCandlesService.GetCandles(broker, usdtMarket, Timeframe.D1, false);
183 | }
184 |
185 | var candles = candlesLookup[(usdtMarket, broker)];
186 | var lastClosedCandleIndex = candles.BinarySearchGetItem(
187 | i => candles[i].CloseTimeTicks, 0, currentDateTimeUtc.Ticks,
188 | BinarySearchMethod.PrevLowerValueOrValue);
189 |
190 | var candle = candles[lastClosedCandleIndex];
191 | var assetUsdtValue = assetBalance.Value.Balance * (decimal)candle.CloseBid;
192 | totalUsdtValue += assetUsdtValue;
193 | }
194 |
195 | return totalUsdtValue;
196 | }
197 | }
198 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/ViewModels/StrategyRunResultsViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.ComponentModel.Composition;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Threading.Tasks;
8 | using System.Windows.Media;
9 | using System.Windows.Threading;
10 | using StrategyEditor.Services;
11 | using Hallupa.Library;
12 | using log4net;
13 | using TraderTools.Basics;
14 | using TraderTools.Core.UI;
15 | using TraderTools.Core.UI.ViewModels;
16 | using TraderTools.Core.UI.Views;
17 | using TraderTools.Indicators;
18 |
19 | namespace StrategyEditor.ViewModels
20 | {
21 | public class StrategyRunResultsViewModel
22 | {
23 | private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
24 | [Import] private StrategyRunnerResultsService _results;
25 | [Import] public IBrokersService _brokersService;
26 | [Import] public UIService _uiService;
27 | private Dispatcher _dispatcher;
28 | private IDisposable _testResultsUpdatedObserver;
29 | private IDisposable _testResultsStartedObserver;
30 |
31 | public StrategyRunResultsViewModel()
32 | {
33 | _dispatcher = Dispatcher.CurrentDispatcher;
34 | ChartViewModel = new ChartViewModel();
35 | ((INotifyPropertyChanged)ChartViewModel).PropertyChanged += ChartViewModelPropertyChanged;
36 | TradesViewModel = new TradeListViewModel();
37 | ChartViewModel.ChartTimeframe = Timeframe.H4;
38 |
39 | DependencyContainer.ComposeParts(this);
40 |
41 | _uiService.ViewTradeObservable.Subscribe(o =>
42 | {
43 | if (TradesViewModel.SelectedTrade == null) return;
44 |
45 | if (TradesViewModel.SelectedTrade.Timeframe != null)
46 | {
47 | ChartViewModel.ChartTimeframe = TradesViewModel.SelectedTrade.Timeframe.Value;
48 | }
49 |
50 | ChartViewModel.ShowTrade(TradesViewModel.SelectedTrade,
51 | ChartViewModel.ChartTimeframeOptions[ChartViewModel.SelectedChartTimeframeIndex], false,
52 | s => { },
53 | new List<(IIndicator Indicator, Color Color, bool ShowInLegend)>()
54 | {
55 | (new ExponentialMovingAverage(8), Colors.DarkBlue, true),
56 | (new ExponentialMovingAverage(25), Colors.Blue, true),
57 | (new ExponentialMovingAverage(50), Colors.Blue, true),
58 | (new BollingerBand(1.5F, 20), Colors.Green, true),
59 | (new BollingerBand(-1.5F, 20), Colors.Green, false)
60 | },
61 | _uiService.UseHeikenAshi);
62 | });
63 |
64 | ResultsViewModel = new TradesResultsViewModel(() =>
65 | {
66 | lock (_results.Results)
67 | {
68 | return _results.Results.ToList();
69 | }
70 | });
71 |
72 | _testResultsStartedObserver = _results.TestRunStarted.Subscribe(newResults =>
73 | {
74 | UpdateTrades();
75 | ResultsViewModel.UpdateResults();
76 | UpdateStatusColumn(newResults.Strategy?.BrokerKind);
77 | });
78 |
79 | _testResultsUpdatedObserver = _results.TestRunCompleted.Subscribe(newResults =>
80 | {
81 | UpdateTrades();
82 | ResultsViewModel.UpdateResults();
83 | UpdateStatusColumn(newResults.Strategy?.BrokerKind);
84 | });
85 |
86 | Task.Run(() =>
87 | {
88 | Log.Info("Updating strategy run results");
89 | UpdateTrades();
90 | ResultsViewModel.UpdateResults();
91 | Log.Info("Updated strategy run results");
92 | });
93 |
94 | TradesViewModel.ShowClosedTrades = true;
95 | TradesViewModel.ShowOpenTrades = true;
96 | TradesViewModel.ShowOrders = true;
97 | TradesViewModel.TradeListDisplayOptions &= ~TradeListDisplayOptionsFlag.Comments
98 | & ~TradeListDisplayOptionsFlag.Strategies;
99 | }
100 |
101 | private void UpdateStatusColumn(BrokerKind? brokerKind)
102 | {
103 | if (brokerKind is BrokerKind.Trade)
104 | {
105 | TradesViewModel.TradeListDisplayOptions &= ~TradeListDisplayOptionsFlag.Status
106 | & ~TradeListDisplayOptionsFlag.PoundsPerPip
107 | & ~TradeListDisplayOptionsFlag.ResultR;
108 | }
109 | else
110 | {
111 | TradesViewModel.TradeListDisplayOptions |= TradeListDisplayOptionsFlag.Status
112 | | TradeListDisplayOptionsFlag.PoundsPerPip
113 | | TradeListDisplayOptionsFlag.ResultR;
114 | }
115 | }
116 |
117 | private void ChartViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
118 | {
119 | if (e.PropertyName == "SelectedChartTimeframeIndex")
120 | {
121 | _uiService?.ViewTradeCommand.Execute(null);
122 | }
123 | }
124 |
125 | public TradeListViewModel TradesViewModel { get; set; }
126 |
127 | public ChartViewModel ChartViewModel { get; set; }
128 |
129 | public TradesResultsViewModel ResultsViewModel { get; }
130 |
131 | private void UpdateTrades()
132 | {
133 | var allTrades = _results.Results.OrderByDescending(x => x.OrderDateTime ?? x.EntryDateTime).ToList();
134 |
135 | _dispatcher.Invoke(() =>
136 | {
137 | // Clear trades in a single operation to speed it up
138 | if (allTrades.Count == 0)
139 | {
140 | TradesViewModel.Trades.Clear();
141 | }
142 |
143 | TradesViewModel.Trades.Clear();
144 |
145 | // Add new trades
146 | if (TradesViewModel.Trades.Count == 0)
147 | {
148 | TradesViewModel.Trades.AddRange(allTrades);
149 | }
150 | else
151 | {
152 | for (var i = 0; i < allTrades.Count; i++)
153 | {
154 | var trade = allTrades[i];
155 | if (i >= TradesViewModel.Trades.Count)
156 | {
157 | TradesViewModel.Trades.Add(trade);
158 | }
159 | else if (trade != TradesViewModel.Trades[i])
160 | {
161 | var existingIndex = TradesViewModel.Trades.IndexOf(trade);
162 | if (existingIndex != -1)
163 | {
164 | TradesViewModel.Trades.Move(existingIndex, i);
165 | }
166 | else
167 | {
168 | TradesViewModel.Trades.Insert(i, trade);
169 | }
170 | }
171 | }
172 | }
173 | });
174 | }
175 | }
176 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/ViewModels/StrategyViewAllTradesViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.ComponentModel.Composition;
5 | using System.Linq;
6 | using System.Runtime.CompilerServices;
7 | using System.Threading.Tasks;
8 | using System.Windows.Forms;
9 | using System.Windows.Threading;
10 | using Hallupa.Library;
11 | using Hallupa.TraderTools.Brokers.Binance;
12 | using SciChart.Charting.Model.DataSeries;
13 | using SciChart.Charting.Visuals.Annotations;
14 | using StrategyEditor.Services;
15 | using TraderTools.Basics;
16 | using TraderTools.Basics.Extensions;
17 | using TraderTools.Core.Extensions;
18 | using TraderTools.Core.UI;
19 | using TraderTools.Core.UI.Services;
20 | using TraderTools.Indicators;
21 |
22 | namespace StrategyEditor.ViewModels
23 | {
24 | public class StrategyViewAllTradesViewModel : INotifyPropertyChanged
25 | {
26 | [Import] private IBrokersService _brokersService;
27 | [Import] private IBrokersCandlesService _candlesService;
28 | [Import] private IDataDirectoryService _dataDirectoryService;
29 | [Import] private IMarketDetailsService _marketDetailsService;
30 | [Import] public ChartingService ChartingService { get; private set; }
31 | [Import] private StrategyRunnerResultsService _results;
32 | private bool _viewPairEnabled = true;
33 | private Dispatcher _dispatcher;
34 | private BinanceBroker _broker;
35 | private IDisposable _testResultsStartedObserver;
36 | private IDisposable _testResultsUpdatedObserver;
37 | private List _trades;
38 | private List _currentCandles;
39 | private string _currentPair;
40 | private bool _isHeikinAshi;
41 | private string _viewPairText;
42 |
43 | public StrategyViewAllTradesViewModel()
44 | {
45 | DependencyContainer.ComposeParts(this);
46 |
47 | _broker = (BinanceBroker)_brokersService.Brokers.First(b => b.Name == "Binance");
48 | _dispatcher = Dispatcher.CurrentDispatcher;
49 | ViewPairCommand = new DelegateCommand(o => ViewPair());
50 | ChartViewModel.ChartTimeframeChangedAction += ChartTimeframeChangedAction;
51 |
52 | _testResultsStartedObserver = _results.TestRunStarted.Subscribe(newResults =>
53 | {
54 | _trades = null;
55 | if (ChartViewModel.ChartPaneViewModels != null && ChartViewModel.ChartPaneViewModels.Count > 0)
56 | ChartViewModel.ChartPaneViewModels[0].TradeAnnotations.Clear();
57 | });
58 |
59 | _testResultsUpdatedObserver = _results.TestRunCompleted.Subscribe(newResults =>
60 | {
61 | _trades = newResults.Trades;
62 | if (_trades.Count > 0)
63 | {
64 | ViewPairText = _trades[0].Market;
65 | }
66 |
67 | ViewPair();
68 | });
69 | }
70 |
71 | public DelegateCommand ViewPairCommand { get; }
72 |
73 | public bool IsHeikinAshi
74 | {
75 | get => _isHeikinAshi;
76 | set
77 | {
78 | _isHeikinAshi = value;
79 | ViewPair(false);
80 | }
81 | }
82 |
83 | public string ViewPairText
84 | {
85 | get => _viewPairText;
86 | set
87 | {
88 | _viewPairText = value;
89 | OnPropertyChanged();
90 | }
91 | }
92 |
93 |
94 | public ChartViewModel ChartViewModel { get; } = new ChartViewModel();
95 |
96 | public bool ViewPairEnabled
97 | {
98 | get => _viewPairEnabled;
99 | set
100 | {
101 | _viewPairEnabled = value;
102 | OnPropertyChanged();
103 | }
104 | }
105 |
106 | public void SetCandlesOnChart(List candles, string pair, string seriesName)
107 | {
108 | ChartViewModel.ChartPaneViewModels.Clear();
109 | ViewPairText = pair;
110 | ChartHelper.SetChartViewModelPriceData(candles, ChartViewModel, seriesName);
111 | }
112 |
113 | private void ChartTimeframeChangedAction()
114 | {
115 | ViewPair(false);
116 | }
117 |
118 | private void ViewPair(bool refreshTradeAnnotations = true)
119 | {
120 | if (string.IsNullOrEmpty(ViewPairText)) return;
121 |
122 | ViewPairEnabled = false;
123 |
124 | Task.Run(() =>
125 | {
126 | var candles = _candlesService.GetDerivedCandles(_broker, ViewPairText, ChartViewModel.ChartTimeframe);
127 | if (candles.Count == 0)
128 | {
129 | _dispatcher.Invoke((Action)(() =>
130 | {
131 | ViewPairEnabled = true;
132 | ChartViewModel.ChartPaneViewModels.Clear();
133 | }));
134 |
135 | return;
136 | }
137 |
138 | if (IsHeikinAshi) candles = candles.CreateHeikinAshiCandles();
139 |
140 | _currentCandles = candles;
141 | _currentPair = ViewPairText;
142 |
143 | var priceDataSeries = new OhlcDataSeries();
144 | var xvalues = new List();
145 | var openValues = new List();
146 | var highValues = new List();
147 | var lowValues = new List();
148 | var closeValues = new List();
149 |
150 | foreach (var c in candles)
151 | {
152 | var time = new DateTime(c.CloseTimeTicks, DateTimeKind.Utc).ToUniversalTime();
153 |
154 | xvalues.Add(time);
155 | openValues.Add((double)c.OpenBid);
156 | highValues.Add((double)c.HighBid);
157 | lowValues.Add((double)c.LowBid);
158 | closeValues.Add((double)c.CloseBid);
159 | }
160 |
161 | priceDataSeries.Append(xvalues, openValues, highValues, lowValues, closeValues);
162 | priceDataSeries.SeriesName = "Price";
163 |
164 | _dispatcher.Invoke(() =>
165 | {
166 | //ChartViewModel.ChartPaneViewModels.Clear();
167 | ChartHelper.SetChartViewModelPriceData(candles, ChartViewModel);//, null);
168 |
169 | if (refreshTradeAnnotations) AddTradeMarkers();
170 |
171 | /*var pricePaneVm = new ChartPaneViewModel(ChartViewModel, ChartViewModel.ViewportManager)
172 | {
173 | IsFirstChartPane = false,
174 | IsLastChartPane = false,
175 | Height = 400
176 | };
177 | ChartViewModel.ChartPaneViewModels.Add(pricePaneVm);
178 |
179 | ChartHelper.SetChartViewModelPriceData(candles, ChartViewModel);//, pricePaneVm, false);*/
180 |
181 | /*var indicatorChartPaneViewModel =
182 | new ChartPaneViewModel(ChartViewModel, ChartViewModel.ViewportManager)
183 | {
184 | IsFirstChartPane = false,
185 | IsLastChartPane = true,
186 | Height = 200
187 | };
188 | */
189 | /*ChartHelper.AddIndicator(
190 | indicatorChartPaneViewModel,
191 | ViewPairText,
192 | new StochasticRelativeStrengthIndex(),
193 | Colors.Blue,
194 | timeframe,
195 | candles);
196 | ChartViewModel.ChartPaneViewModels.Add(indicatorChartPaneViewModel);*/
197 |
198 | //ChartHelper.add
199 | /*ChartPaneViewModels[0].ChartSeriesViewModels.Clear();
200 | ChartPaneViewModels[0].ChartSeriesViewModels.Add(new ChartSeriesViewModel(
201 | priceDataSeries,
202 | new FastCandlestickRenderableSeries
203 | {
204 | AntiAliasing = false
205 | }));*/
206 |
207 | /*var indicatorSeries = ChartHelper.CreateIndicatorSeries(
208 | ViewPairText,
209 | new T3CommodityChannelIndex(),
210 | Colors.Blue,
211 | timeframe,
212 | candles);
213 |
214 | var indicatorViewModel = new ChartSeriesViewModel(indicatorSeries, new FastLineRenderableSeries
215 | {
216 | AntiAliasing = false,
217 | SeriesColor = Colors.Blue,
218 | StrokeThickness = 2
219 | });*/
220 |
221 | /*var pvm = new ChartPaneViewModel(ChartViewModel, ChartViewModel.ViewportManager)
222 | {
223 | IsFirstChartPane = false,
224 | IsLastChartPane = true,
225 | Height = 100
226 | };*/
227 |
228 | //ChartSeriesViewModels.Add(indicatorViewModel);
229 |
230 | Task.Run(() =>
231 | {
232 | _dispatcher.Invoke((Action)(() =>
233 | {
234 | /*var min = priceDataSeries.Count - 220;
235 | var max = priceDataSeries.Count + 5;
236 |
237 | if (min < 0) min = 0;
238 | if (min <= XVisibleRange.Max)
239 | {
240 | XVisibleRange.Min = min;
241 | XVisibleRange.Max = max;
242 | }
243 | else
244 | {
245 | XVisibleRange.Max = max;
246 | XVisibleRange.Min = min;
247 | }*/
248 |
249 | ViewPairEnabled = true;
250 | }));
251 | });
252 | });
253 | });
254 | }
255 |
256 | private void AddTradeMarkers()
257 | {
258 | if (ChartViewModel.ChartPaneViewModels != null && ChartViewModel.ChartPaneViewModels.Count > 0)
259 | {
260 | var annotations = new AnnotationCollection();
261 |
262 | var l = new List();
263 | foreach (var t in _trades)
264 | {
265 | if (t.Market != _currentPair || t.TradeDirection == null) continue;
266 |
267 | if (t.EntryDateTime != null && t.EntryPrice != null)
268 | {
269 | l.Add(ChartHelper.CreateBuySellMarker(t.TradeDirection.Value, null, t.EntryDateTime.Value, t.EntryPrice.Value, true));
270 | }
271 |
272 | if (t.CloseDateTime != null && t.ClosePrice != null)
273 | {
274 | l.Add(ChartHelper.CreateBuySellMarker(t.TradeDirection.Value == TradeDirection.Long ? TradeDirection.Short : TradeDirection.Long,
275 | null, t.CloseDateTime.Value, t.ClosePrice.Value, true));
276 | }
277 | }
278 |
279 | ChartViewModel.ChartPaneViewModels[0].TradeAnnotations = new AnnotationCollection(l);
280 | }
281 | }
282 |
283 | public event PropertyChangedEventHandler PropertyChanged;
284 |
285 | protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
286 | {
287 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
288 | }
289 | }
290 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/CreatePointGroupView.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/CreatePointGroupView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace StrategyEditor.Views
4 | {
5 | ///
6 | /// Interaction logic for CreatePointGroupView.xaml
7 | ///
8 | public partial class CreatePointGroupView : Window
9 | {
10 | public CreatePointGroupView()
11 | {
12 | InitializeComponent();
13 | }
14 |
15 | public bool OKClicked { get; private set; }
16 |
17 | private void OkClicked(object sender, RoutedEventArgs e)
18 | {
19 | OKClicked = true;
20 | Close();
21 | }
22 |
23 | private void CancelClicked(object sender, RoutedEventArgs e)
24 | {
25 | Close();
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/CreatePointView.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/CreatePointView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using StrategyEditor.ViewModels;
3 |
4 | namespace StrategyEditor.Views
5 | {
6 | ///
7 | /// Interaction logic for CreatePointView.xaml
8 | ///
9 | public partial class CreatePointView : Window
10 | {
11 | public CreatePointView()
12 | {
13 | InitializeComponent();
14 |
15 | Model = new CreatePointViewModel(Close);
16 | DataContext = Model;
17 | }
18 |
19 | public CreatePointViewModel Model { get; private set; }
20 | }
21 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/LoginView.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/LoginView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace StrategyEditor.Views
4 | {
5 | ///
6 | /// Interaction logic for LoginView.xaml
7 | ///
8 | public partial class LoginView : Window
9 | {
10 | public LoginView()
11 | {
12 | InitializeComponent();
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/MachineLearningView.xaml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
75 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/MachineLearningView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 | using StrategyEditor.ViewModels;
3 |
4 | namespace StrategyEditor.Views
5 | {
6 | ///
7 | /// Interaction logic for MachineLearningView.xaml
8 | ///
9 | public partial class MachineLearningView : Page
10 | {
11 | public MachineLearningView()
12 | {
13 | InitializeComponent();
14 |
15 | DataContext = new MachineLearningViewModel();
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/ProgressView.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 | Test
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/ProgressView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace StrategyEditor.Views
4 | {
5 | ///
6 | /// Interaction logic for ProgressView.xaml
7 | ///
8 | public partial class ProgressView : Window
9 | {
10 | public ProgressView()
11 | {
12 | InitializeComponent();
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/StrategyRunResultsChartView.xaml:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
34 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/StrategyRunResultsChartView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 | using StrategyEditor.ViewModels;
3 |
4 | namespace StrategyEditor.Views
5 | {
6 | ///
7 | /// Interaction logic for StrategyRunResultsChartView.xaml
8 | ///
9 | public partial class StrategyRunResultsChartView : Page
10 | {
11 | public StrategyRunResultsChartView()
12 | {
13 | InitializeComponent();
14 |
15 | DataContext = new StrategyRunResultsChartViewModel();
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/StrategyRunResultsView.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/StrategyRunResultsView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 | using StrategyEditor.ViewModels;
3 |
4 | namespace StrategyEditor.Views
5 | {
6 | ///
7 | /// Interaction logic for StrategyRunResultsView.xaml
8 | ///
9 | public partial class StrategyRunResultsView : Page
10 | {
11 | public StrategyRunResultsView()
12 | {
13 | InitializeComponent();
14 |
15 | DataContext = new StrategyRunResultsViewModel();
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/StrategyRunView.xaml:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/StrategyRunView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 | using StrategyEditor.ViewModels;
3 |
4 | namespace StrategyEditor.Views
5 | {
6 | ///
7 | /// Interaction logic for StrategyRunView.xaml
8 | ///
9 | public partial class StrategyRunView : Page
10 | {
11 | public StrategyRunView()
12 | {
13 | InitializeComponent();
14 |
15 | DataContext = new StrategyRunViewModel();
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/StrategyViewAllTradesView.xaml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Src/StrategyEditor/Views/StrategyViewAllTradesView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 | using StrategyEditor.ViewModels;
3 |
4 | namespace StrategyEditor.Views
5 | {
6 | ///
7 | /// Interaction logic for StrategyViewAllTradesView.xaml
8 | ///
9 | public partial class StrategyViewAllTradesView : Page
10 | {
11 | public StrategyViewAllTradesView()
12 | {
13 | InitializeComponent();
14 |
15 | DataContext = new StrategyViewAllTradesViewModel();
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/Src/StrategyEditor/log4net.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.Composition;
4 | using System.Configuration;
5 | using System.Data;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Reflection;
9 | using System.Threading.Tasks;
10 | using System.Windows;
11 | using Hallupa.Library;
12 | using log4net;
13 | using log4net.Config;
14 | using TraderTools.Basics;
15 | using TraderTools.Core.Services;
16 | using TraderTools.Core.UI.Services;
17 | using TraderTools.Simulation;
18 |
19 | namespace StrategyRunnerLive
20 | {
21 | ///
22 | /// Interaction logic for App.xaml
23 | ///
24 | public partial class App : Application
25 | {
26 | private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
27 |
28 | [Import] private IDataDirectoryService _dataDirectoryService;
29 |
30 | protected override void OnStartup(StartupEventArgs e)
31 | {
32 | try
33 | {
34 | var logRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
35 | XmlConfigurator.Configure(logRepository, new FileInfo("log4net.config"));
36 |
37 | Log.Info("Starting application");
38 |
39 | DependencyContainer.AddAssembly(typeof(App).Assembly);
40 | DependencyContainer.AddAssembly(typeof(BrokersService).Assembly);
41 | DependencyContainer.AddAssembly(typeof(ChartingService).Assembly);
42 | DependencyContainer.AddAssembly(typeof(ModelPredictorService).Assembly);
43 |
44 | DependencyContainer.ComposeParts(this);
45 |
46 | _dataDirectoryService.SetApplicationName("AutomatedTrader");
47 |
48 | Log.Info($"Using main directory: {_dataDirectoryService.MainDirectoryWithApplicationName}");
49 | if (!Directory.Exists(_dataDirectoryService.MainDirectoryWithApplicationName))
50 | {
51 | Directory.CreateDirectory(_dataDirectoryService.MainDirectoryWithApplicationName);
52 | }
53 | }
54 | catch (Exception ex)
55 | {
56 | Log.Error("Failed on startup", ex);
57 | throw;
58 | }
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/AppSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Binance": {
3 | "ApiKey" : "",
4 | "SecretKey": ""
5 | }
6 | }
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | [assembly: ThemeInfo(
4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
5 | //(used if a resource is not found in the page,
6 | // or application resource dictionaries)
7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
8 | //(used if a resource is not found in the page,
9 | // app, or any theme specific resource dictionaries)
10 | )]
11 |
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using StrategyRunnerLive.ViewModels;
3 |
4 | namespace StrategyRunnerLive
5 | {
6 | ///
7 | /// Interaction logic for MainWindow.xaml
8 | ///
9 | public partial class MainWindow : Window
10 | {
11 | public MainWindow()
12 | {
13 | InitializeComponent();
14 |
15 | DataContext = new MainWindowViewModel();
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace StrategyRunnerLive {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 |
26 | [global::System.Configuration.UserScopedSettingAttribute()]
27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
28 | [global::System.Configuration.DefaultSettingValueAttribute("")]
29 | public string Username {
30 | get {
31 | return ((string)(this["Username"]));
32 | }
33 | set {
34 | this["Username"] = value;
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/StrategyRunnerLive.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net6.0-windows
6 | true
7 | 1.0.1
8 | AnyCPU;x64
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | True
35 | True
36 | Settings.settings
37 |
38 |
39 |
40 |
41 |
42 | PreserveNewest
43 |
44 |
45 | PreserveNewest
46 |
47 |
48 | SettingsSingleFileGenerator
49 | Settings.Designer.cs
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/ViewModels/LoginOutViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.ComponentModel.Composition;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Runtime.CompilerServices;
7 | using System.Threading.Tasks;
8 | using System.Windows;
9 | using System.Windows.Threading;
10 | using Hallupa.Library;
11 | using log4net;
12 | using StrategyRunnerLive.Views;
13 | using TraderTools.Basics;
14 | using TraderTools.Brokers.FXCM;
15 |
16 | namespace StrategyRunnerLive.ViewModels
17 | {
18 | public class LoginOutViewModel : INotifyPropertyChanged
19 | {
20 | private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
21 |
22 | private string _loginOutButtonText;
23 | private bool _loginOutButtonEnabled = true;
24 | private readonly FxcmBroker _fxcm;
25 | private Action> _createLoginViewFunc;
26 | private Func<(Action show, Action updateText, Action close)> _createProgressingViewFunc;
27 | private Dispatcher _dispatcher;
28 |
29 | [Import] private IBrokersService _brokersService;
30 | [Import] private IMarketDetailsService _marketsService;
31 |
32 | public LoginOutViewModel()
33 | {
34 | DependencyContainer.ComposeParts(this);
35 |
36 | LoginOutCommand = new DelegateCommand(o => LoginOut(), o => LoginOutButtonEnabled);
37 | _loginOutButtonText = "Login";
38 | _fxcm = (FxcmBroker)_brokersService.Brokers.First(x => x.Name == "FXCM");
39 | _dispatcher = Dispatcher.CurrentDispatcher;
40 |
41 | _createLoginViewFunc = loginAction =>
42 | {
43 | var view = new LoginView { Owner = Application.Current.MainWindow };
44 | var loginVm = new LoginViewModel(() => view.Close(), loginAction);
45 | view.DataContext = loginVm;
46 | view.ShowDialog();
47 | };
48 |
49 | _createProgressingViewFunc = () =>
50 | {
51 | var view = new ProgressView { Owner = Application.Current.MainWindow };
52 |
53 | return (text =>
54 | {
55 | view.TextToShow.Text = text;
56 | view.ShowDialog();
57 | },
58 | txt =>
59 | {
60 | if (_dispatcher.CheckAccess())
61 | {
62 | view.TextToShow.Text = txt;
63 | }
64 | else
65 | {
66 | _dispatcher.BeginInvoke((Action)(() => { view.TextToShow.Text = txt; }));
67 | }
68 | },
69 | () => view.Close());
70 | };
71 | }
72 |
73 | public string LoginOutButtonText
74 | {
75 | get => _loginOutButtonText;
76 | set
77 | {
78 | _loginOutButtonText = value;
79 | OnPropertyChanged();
80 | }
81 | }
82 |
83 | public bool LoginOutButtonEnabled
84 | {
85 | get => _loginOutButtonEnabled;
86 | set
87 | {
88 | _loginOutButtonEnabled = value;
89 | LoginOutCommand.RaiseCanExecuteChanged();
90 | OnPropertyChanged();
91 | }
92 | }
93 |
94 | public DelegateCommand LoginOutCommand { get; }
95 |
96 |
97 | public event PropertyChangedEventHandler PropertyChanged;
98 |
99 | protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
100 | {
101 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
102 | }
103 |
104 | private void LoginOut()
105 | {
106 | LoginOutButtonEnabled = false;
107 | var loginAttempted = false;
108 |
109 | if (_fxcm.Status != ConnectStatus.Connected)
110 | {
111 | var progressViewActions = _createProgressingViewFunc();
112 |
113 | _createLoginViewFunc((username, password, connection) =>
114 | {
115 | Task.Run(() =>
116 | {
117 | try
118 | {
119 | loginAttempted = true;
120 | _fxcm.SetUsernamePassword(username, password, connection);
121 | _fxcm.Connect();
122 |
123 | if (_fxcm.Status == ConnectStatus.Connected)
124 | {
125 | var marketDetailsList = _fxcm.GetMarketDetailsList();
126 | if (marketDetailsList != null)
127 | {
128 | foreach (var marketDetails in marketDetailsList)
129 | {
130 | _marketsService.AddMarketDetails(marketDetails);
131 | }
132 |
133 | _marketsService.SaveMarketDetailsList();
134 | }
135 | }
136 | }
137 | catch (Exception ex)
138 | {
139 | Log.Error("Unable to login", ex);
140 | }
141 |
142 | _dispatcher.Invoke(() =>
143 | {
144 | progressViewActions.close();
145 | });
146 | });
147 |
148 | progressViewActions.show("Logging in...");
149 | });
150 | }
151 | else
152 | {
153 | var progressViewActions = _createProgressingViewFunc();
154 |
155 | Task.Run(() =>
156 | {
157 | _fxcm.Disconnect();
158 |
159 | _dispatcher.Invoke(() =>
160 | {
161 | progressViewActions.close();
162 | LoginOutButtonEnabled = true;
163 | UpdateLoginButtonText();
164 | });
165 | });
166 |
167 | progressViewActions.show("Logging out...");
168 | }
169 |
170 | LoginOutButtonEnabled = true;
171 |
172 | UpdateLoginButtonText();
173 | if (_fxcm.Status != ConnectStatus.Connected && loginAttempted)
174 | {
175 | MessageBox.Show("Unable to login", "Failed", MessageBoxButton.OK);
176 | }
177 | }
178 |
179 | private void UpdateLoginButtonText()
180 | {
181 | if (_fxcm.Status == ConnectStatus.Connected)
182 | {
183 | LoginOutButtonText = "Logout";
184 | }
185 | else
186 | {
187 | LoginOutButtonText = "Login";
188 | }
189 | }
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/ViewModels/LoginViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Controls;
3 | using Hallupa.Library;
4 |
5 | namespace StrategyRunnerLive.ViewModels
6 | {
7 | public class LoginViewModel
8 | {
9 | private readonly Action _closeViewAction;
10 | private readonly Action _loginAction;
11 |
12 | public LoginViewModel(Action closeViewAction, Action loginAction)
13 | {
14 | Username = Settings.Default.Username;
15 |
16 | _closeViewAction = closeViewAction;
17 | _loginAction = loginAction;
18 | LoginCommand = new DelegateCommand(Login);
19 | }
20 |
21 | public DelegateCommand LoginCommand { get; }
22 |
23 | public string Username { get; set; }
24 |
25 | public string Connection { get; set; } = "GBREAL";
26 |
27 | private void Login(object obj)
28 | {
29 | var passwordBox = (PasswordBox)obj;
30 | var password = passwordBox.Password;
31 | _closeViewAction();
32 |
33 | Settings.Default.Username = Username;
34 | Settings.Default.Save();
35 |
36 | _loginAction(Username, password, Connection);
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/ViewModels/MainWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.ObjectModel;
3 | using System.ComponentModel.Composition;
4 | using System.IO;
5 | using System.Reflection;
6 | using System.Threading.Tasks;
7 | using Hallupa.Library;
8 | using Hallupa.TraderTools.Brokers.Binance;
9 | using log4net;
10 | using TraderTools.Basics;
11 | using TraderTools.Brokers.FXCM;
12 | using TraderTools.Core.Services;
13 |
14 | namespace StrategyRunnerLive.ViewModels
15 | {
16 | public class MainWindowViewModel
17 | {
18 | private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
19 | private FxcmBroker _fxcm;
20 | private string _strategiesDirectory;
21 |
22 | [Import] private IBrokersService _brokersService;
23 | [Import] private IDataDirectoryService _dataDirectoryService;
24 | [Import] private IBrokersCandlesService _candlesService;
25 | [Import] private IMarketDetailsService _marketDetailsService;
26 | [Import] private ITradeDetailsAutoCalculatorService _tradeDetailsAutoCalculatorService;
27 |
28 | private string _selectedStrategyFilename;
29 |
30 | public MainWindowViewModel()
31 | {
32 | Log.Info("Application started");
33 |
34 | try
35 | {
36 | DependencyContainer.ComposeParts(this);
37 |
38 | _strategiesDirectory = Path.Combine(_dataDirectoryService.MainDirectoryWithApplicationName);
39 |
40 | if (!Directory.Exists(_strategiesDirectory))
41 | {
42 | Directory.CreateDirectory(_strategiesDirectory);
43 | }
44 |
45 | var binance = new BinanceBroker();
46 |
47 | // Setup brokers
48 | var brokers = new IBroker[]
49 | {
50 | _fxcm = new FxcmBroker
51 | {
52 | IncludeReportInUpdates = false
53 | },
54 | binance
55 | };
56 |
57 | _brokersService.AddBrokers(brokers);
58 |
59 | var logDirectory = DataDirectoryService.GetMainDirectoryWithApplicationName("TradeLog");
60 | _brokersService.LoadBrokerAccounts(_tradeDetailsAutoCalculatorService, logDirectory);
61 |
62 | var account = _brokersService.AccountsLookup[binance];
63 |
64 | Task.Run(() =>
65 | {
66 | // TODO - threading
67 | /*account.UpdateBrokerAccount(
68 | binance,
69 | _candlesService,
70 | _marketDetailsService,
71 | _tradeDetailsAutoCalculatorService);
72 | account.SaveAccount(DataDirectoryService.GetMainDirectoryWithApplicationName("TradeLog"));*/
73 |
74 | });
75 |
76 | RunStrategyLiveViewModel = new RunStrategyLiveViewModel();
77 |
78 | LoginOutViewModel = new LoginOutViewModel();
79 |
80 | RefreshStrategyFilenames();
81 | }
82 | catch (Exception ex)
83 | {
84 | Log.Error("Failed to create MainWindowViewModel", ex);
85 | throw;
86 | }
87 | }
88 |
89 | public LoginOutViewModel LoginOutViewModel { get; private set; }
90 |
91 | public RunStrategyLiveViewModel RunStrategyLiveViewModel { get; private set; }
92 |
93 | public string SelectedStrategyFilename
94 | {
95 | get => _selectedStrategyFilename;
96 | set
97 | {
98 | _selectedStrategyFilename = value;
99 | RunStrategyLiveViewModel.SelectedStrategyFilename = _selectedStrategyFilename;
100 | }
101 | }
102 |
103 | public ObservableCollection StrategyFilenames { get; } = new ObservableCollection();
104 |
105 | private void RefreshStrategyFilenames()
106 | {
107 | StrategyFilenames.Clear();
108 | var strategyPaths = Directory.GetFiles(_strategiesDirectory, "*.cs");
109 | foreach (var strategyPath in strategyPaths)
110 | {
111 | StrategyFilenames.Add(Path.GetFileNameWithoutExtension(strategyPath));
112 | }
113 | }
114 | }
115 | }
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/ViewModels/RunStrategyLiveViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.Composition;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 | using System.Windows;
10 | using Hallupa.Library;
11 | using Hallupa.TraderTools.Brokers.Binance;
12 | using Hallupa.TraderTools.Simulation;
13 | using log4net;
14 | using TraderTools.Basics;
15 | using TraderTools.Basics.Extensions;
16 | using TraderTools.Core.Services;
17 | using TraderTools.Simulation;
18 |
19 | namespace StrategyRunnerLive.ViewModels
20 | {
21 | public class RunStrategyLiveViewModel
22 | {
23 | private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
24 | private bool _runningLive = false;
25 | private string _strategiesDirectory;
26 | private string _logDirectory;
27 |
28 | [Import] private IBrokersService _brokersService;
29 | [Import] private IDataDirectoryService _dataDirectoryService;
30 | [Import] private ITradeDetailsAutoCalculatorService _tradeDetailsAutoCalculatorService;
31 | [Import] private IBrokersCandlesService _candlesService;
32 | [Import] private IMarketDetailsService _marketDetailsService;
33 |
34 | public RunStrategyLiveViewModel()
35 | {
36 | DependencyContainer.ComposeParts(this);
37 |
38 | RunLiveCommand = new DelegateCommand(o => RunLive());
39 | _strategiesDirectory = _dataDirectoryService.MainDirectoryWithApplicationName;
40 | _logDirectory = DataDirectoryService.GetMainDirectoryWithApplicationName("TradeLog");
41 | _brokersService.LoadBrokerAccounts(_tradeDetailsAutoCalculatorService, _logDirectory);
42 | }
43 |
44 | public DelegateCommand RunLiveCommand { get; }
45 |
46 | public string SelectedStrategyFilename { get; set; }
47 |
48 | private void RunLive()
49 | {
50 | /*if (_fxcm.Status != ConnectStatus.Connected)
51 | {
52 | MessageBox.Show(Application.Current.MainWindow, "FXCM not connected", "Cannot run live");
53 | return;
54 | }*/
55 |
56 | if (string.IsNullOrEmpty(SelectedStrategyFilename))
57 | {
58 | MessageBox.Show(Application.Current.MainWindow, "No strategy selected", "Cannot run live");
59 | return;
60 | }
61 |
62 | if (_runningLive)
63 | {
64 | MessageBox.Show(Application.Current.MainWindow, "Strategy already running", "Cannot run live");
65 | return;
66 | }
67 |
68 | _runningLive = true;
69 | Task.Run(() =>
70 | {
71 | try
72 | {
73 | RunLive(SelectedStrategyFilename);
74 | }
75 | catch (Exception ex)
76 | {
77 | Log.Error("Error running strategy", ex);
78 | }
79 |
80 | Log.Info("Finished running strategy live");
81 | });
82 | }
83 |
84 |
85 | private void RunLive(string selectedStrategyFilename)
86 | {
87 | var trades = new TradeWithIndexingCollection();
88 | var candlesLookup = new Dictionary>>();
89 | var accountSaveIntervalSeconds = 60;
90 |
91 | Log.Info("Running live");
92 |
93 | // Get strategy type and markets
94 | var strategyType = CompileStrategyAndGetStrategyMarkets(selectedStrategyFilename);
95 | if (strategyType == null) return;
96 |
97 | var strategy = (StrategyBase)Activator.CreateInstance(strategyType);
98 |
99 | // Get broker and broker account
100 | var broker = (BinanceBroker)_brokersService.Brokers.First(x => x.Name == strategy.Broker);
101 |
102 |
103 | // Get candles
104 | Log.Info("Getting candles");
105 | foreach (var m in strategy.Markets)
106 | {
107 | candlesLookup[m] = new TimeframeLookup>();
108 |
109 | foreach (var t in strategy.Timeframes)
110 | {
111 | candlesLookup[m].Add(t,
112 | _candlesService.GetCandles(broker, m, t, true, forceUpdate: true, cacheData: true)
113 | .Where(c => c.IsComplete == 1)
114 | .ToList());
115 | }
116 | }
117 |
118 | // Create strategies
119 | strategy.IsLive = true;
120 | strategy.SetSimulationParameters(trades, candlesLookup);
121 | strategy.SetInitialised(
122 | true,
123 | () => broker.GetBalance(),
124 | (indexing, trade, arg3) => { },
125 | broker,
126 | _brokersService);
127 |
128 | // Update indicators
129 | var a = new List();
130 | foreach (var m in strategy.Markets)
131 | {
132 | foreach (var t in strategy.Timeframes)
133 | {
134 | var candles = _candlesService.GetCandles(broker, m, t, true, forceUpdate: false)
135 | .Where(c => c.IsComplete == 1)
136 | .ToList();
137 |
138 | foreach (var c in candles)
139 | {
140 | a.Add(new AddedCandleTimeframe(m, t, c));
141 | }
142 | }
143 | }
144 | strategy.UpdateIndicators(a);
145 |
146 | strategy.UpdateBalances();
147 | strategy.Starting();
148 |
149 | Log.Info("Running main processing loop");
150 | while (true)
151 | {
152 | // Update candles
153 | var addedCandles = new List();
154 | foreach (var m in strategy.Markets)
155 | {
156 | foreach (var t in strategy.Timeframes)
157 | {
158 | var candles = _candlesService.GetCandles(broker, m, t, true, forceUpdate: true)
159 | .Where(c => c.IsComplete == 1)
160 | .ToList();
161 |
162 | if (candlesLookup[m][t].Count < candles.Count)
163 | {
164 | for (var i = candlesLookup[m][t].Count; i < candles.Count; i++)
165 | {
166 | candlesLookup[m][t].Add(candles[i]);
167 | addedCandles.Add(new AddedCandleTimeframe(m, t, candles[i]));
168 | }
169 | }
170 | }
171 | }
172 |
173 | if (addedCandles.Any()) // TODO reduce times this is called and include save
174 | {
175 | // Update broker account
176 | Log.Debug("Updating and saving broker account");
177 |
178 | // Update strategy
179 | strategy.UpdateIndicators(addedCandles);
180 | strategy.UpdateBalances();
181 | strategy.ProcessCandles(addedCandles);
182 | }
183 |
184 | Thread.Sleep(10000);
185 | }
186 | }
187 |
188 | private static void ProcessNewPrice(string[] markets, Timeframe[] timeframes,
189 | (string Instrument, double Bid, double Ask, DateTime Time) p, Dictionary>> candles)
190 | {
191 | if (markets.Contains(p.Instrument))
192 | {
193 | foreach (var timeframe in timeframes)
194 | {
195 | lock (candles[p.Instrument][timeframe])
196 | {
197 | var c = candles[p.Instrument][timeframe].Last();
198 | var updated = false;
199 |
200 | // Update existing candle
201 | if (p.Time.Ticks <= c.CloseTimeTicks && p.Time.Ticks >= c.OpenTimeTicks)
202 | {
203 | if (p.Ask > c.HighAsk) c.HighAsk = (float)p.Ask;
204 | if (p.Ask < c.LowAsk) c.LowAsk = (float)p.Ask;
205 | if (p.Bid > c.HighBid) c.HighBid = (float)p.Bid;
206 | if (p.Bid < c.LowBid) c.LowBid = (float)p.Bid;
207 |
208 | c.CloseAsk = (float)p.Ask;
209 | c.CloseBid = (float)p.Bid;
210 | updated = true;
211 | }
212 |
213 | // If time is after current candle - set complete
214 | if (p.Time.Ticks >= c.CloseTimeTicks)
215 | {
216 | Log.Info($"Market {p.Instrument} has complete {timeframe} candle");
217 | c.IsComplete = 1;
218 | updated = true;
219 | }
220 |
221 | if (updated) candles[p.Instrument][timeframe][candles[p.Instrument][timeframe].Count - 1] = c;
222 |
223 | // Add new candle if required
224 | if (p.Time.Ticks >= c.CloseTimeTicks)
225 | {
226 | var targetStartTime = c.CloseTime();
227 | var targetEndTime = targetStartTime.AddSeconds((int)timeframe);
228 |
229 | while (true)
230 | {
231 | if (p.Time.Ticks >= targetStartTime.Ticks && p.Time.Ticks <= targetEndTime.Ticks)
232 | {
233 | candles[p.Instrument][timeframe].Add(new Candle
234 | {
235 | OpenAsk = (float)p.Ask,
236 | CloseAsk = (float)p.Ask,
237 | OpenBid = (float)p.Bid,
238 | CloseBid = (float)p.Bid,
239 | CloseTimeTicks = targetEndTime.Ticks,
240 | OpenTimeTicks = targetStartTime.Ticks,
241 | IsComplete = 0,
242 | LowBid = (float)p.Bid,
243 | HighAsk = (float)p.Ask,
244 | HighBid = (float)p.Bid,
245 | LowAsk = (float)p.Ask
246 | });
247 |
248 | break;
249 | }
250 |
251 | targetEndTime = targetEndTime.AddSeconds((int)timeframe);
252 | targetStartTime = targetStartTime.AddSeconds((int)timeframe);
253 | }
254 | }
255 | }
256 | }
257 | }
258 | }
259 |
260 | private Type CompileStrategyAndGetStrategyMarkets(string selectedStrategyFilename)
261 | {
262 | // Compile strategy
263 | var code = File.ReadAllText(Path.Combine(_strategiesDirectory, $"{selectedStrategyFilename}.cs"));
264 | var strategyType = StrategyHelper.CompileStrategy(code, selectedStrategyFilename);
265 |
266 | if (strategyType == null)
267 | {
268 | Log.Error("Failed to compile strategy");
269 | return strategyType;
270 | }
271 |
272 | return strategyType;
273 | }
274 |
275 | /*
276 | private void UpdateFXCMOpenTradesStops(StrategyBase strategy, Dictionary beforeStopLossLookup)
277 | {
278 | foreach (var t in strategy.Trades.OpenTrades.Where(x => strategy.NewTrades.All(z => z.Id != x.Trade.Id)))
279 | {
280 | if (t.Trade.StopPrice != beforeStopLossLookup[t.Trade.Id])
281 | {
282 | if (!_fxcm.ChangeStop(t.Trade.StopPrices.Last(x => !string.IsNullOrEmpty(x.Id)).Id,
283 | (double)t.Trade.StopPrice.Value))
284 | {
285 | Log.Error($"Unable to change stop price for trade Id: {t.Trade.Id}");
286 | }
287 | }
288 | }
289 | }
290 |
291 | private void CreateNewFXCMTradesAndUpdateAccount(StrategyBase strategy)
292 | {
293 | var newOrderIds = new List();
294 | foreach (var trade in strategy.NewTrades.Where(t => t.EntryPrice != null))
295 | {
296 | if (_fxcm.CreateMarketOrder(trade.Market, 1, trade.TradeDirection.Value, out var orderId,
297 | (double)trade.StopPrice))
298 | {
299 | newOrderIds.Add(orderId);
300 | }
301 | }
302 |
303 | if (newOrderIds.Any())
304 | {
305 | _brokerAccount.UpdateBrokerAccount(_fxcm, _candlesService, _marketDetailsService, _tradeDetailsAutoCalculatorService, UpdateOption.ForceUpdate);
306 |
307 | foreach (var orderId in newOrderIds)
308 | {
309 | var newTrade = _brokerAccount.Trades.FirstOrDefault(t => t.OrderId == orderId);
310 | if (newTrade != null)
311 | {
312 | newTrade.Strategies = SelectedStrategyFilename;
313 | newTrade.Comments = "Created by auto trader";
314 | }
315 |
316 | _brokerAccount.SaveAccount(DataDirectoryService.GetMainDirectoryWithApplicationName("TradeLog"));
317 | strategy.Trades.AddTrade(newTrade);
318 | }
319 | }
320 | }
321 |
322 | private Timeframe[] GetStrategyTimeframes(Type strategyType)
323 | {
324 | var strategy = (StrategyBase)Activator.CreateInstance(strategyType);
325 | return strategy.Timeframes;
326 | }*/
327 | }
328 | }
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/Views/LoginView.xaml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/Views/LoginView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Windows;
5 | using System.Windows.Controls;
6 | using System.Windows.Data;
7 | using System.Windows.Documents;
8 | using System.Windows.Input;
9 | using System.Windows.Media;
10 | using System.Windows.Media.Imaging;
11 | using System.Windows.Shapes;
12 |
13 | namespace StrategyRunnerLive.Views
14 | {
15 | ///
16 | /// Interaction logic for LoginView.xaml
17 | ///
18 | public partial class LoginView : Window
19 | {
20 | public LoginView()
21 | {
22 | InitializeComponent();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/Views/ProgressView.xaml:
--------------------------------------------------------------------------------
1 |
11 |
12 | Test
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/Views/ProgressView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace StrategyRunnerLive.Views
4 | {
5 | ///
6 | /// Interaction logic for ProgressView.xaml
7 | ///
8 | public partial class ProgressView : Window
9 | {
10 | public ProgressView()
11 | {
12 | InitializeComponent();
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Src/StrategyRunnerLive/log4net.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Src/TraderTools.Simulation.Test/TradeAmountUpdaterTests.cs:
--------------------------------------------------------------------------------
1 | /* TODO using System.Collections.Generic;
2 | using FluentAssertions;
3 | using Hallupa.TraderTools.Basics;
4 | using Hallupa.TraderTools.Simulation;
5 | using NUnit.Framework;
6 | using TraderTools.Basics;
7 |
8 | namespace TraderTools.Simulation.Test
9 | {
10 | public class TradeAmountUpdaterTests
11 | {
12 | [SetUp]
13 | public void Setup()
14 | {
15 | }
16 |
17 | [Test]
18 | public void UpdateTradeAndBalance_WhenLongEntryQuantityMoreThanBalance_UpdatesTradeAndBalance()
19 | {
20 | // Arrange
21 | var sut = new TradeAmountUpdater();
22 | var commission = 0.01M;
23 | var ethPrice = 1000M;
24 | var usdtBalance = 10000M;
25 | Dictionary balances = new Dictionary
26 | {
27 | { "USDT", new AssetBalance("USDT", usdtBalance) }
28 | };
29 |
30 | var trade = new Trade
31 | {
32 | EntryQuantity = 100,
33 | EntryPrice = ethPrice,
34 | BaseAsset = "ETH",
35 | Market = "ETHUSDT",
36 | TradeDirection = TradeDirection.Long
37 | };
38 |
39 | var maxBuyCostExcludingFee = usdtBalance / (1M + commission);
40 | var maxEthQuantity = maxBuyCostExcludingFee / ethPrice;
41 |
42 | // Act
43 | sut.UpdateTradeAndBalance(trade, commission, BrokerKind.Trade, balances);
44 |
45 | // Assert
46 | trade.Should().BeEquivalentTo(
47 | new Trade
48 | {
49 | EntryQuantity = maxEthQuantity,
50 | EntryPrice = ethPrice,
51 | BaseAsset = "ETH",
52 | Market = "ETHUSDT",
53 | TradeDirection = TradeDirection.Long,
54 | RiskAmount = 0M
55 | });
56 |
57 | balances["USDT"].Should().BeEquivalentTo(new AssetBalance("USDT", 0M));
58 | balances["ETH"].Should().BeEquivalentTo(new AssetBalance("ETH", maxEthQuantity));
59 | }
60 |
61 | [Test]
62 | public void UpdateTradeAndBalance_WhenLongEntryQuantityLessThanBalance_UpdatesTradeAndBalance()
63 | {
64 | // Arrange
65 | var sut = new TradeAmountUpdater();
66 | var commission = 0.01M;
67 | var ethPrice = 1000M;
68 | var quantity = 3;
69 | var usdtBalance = 10000M;
70 | Dictionary balances = new Dictionary
71 | {
72 | { "USDT", new AssetBalance("USDT", usdtBalance) }
73 | };
74 |
75 | var trade = new Trade
76 | {
77 | EntryQuantity = quantity,
78 | EntryPrice = ethPrice,
79 | BaseAsset = "ETH",
80 | Market = "ETHUSDT",
81 | TradeDirection = TradeDirection.Long
82 | };
83 |
84 | var buyCostExcludingFee = quantity * ethPrice;
85 | var fee = buyCostExcludingFee * commission;
86 |
87 | // Act
88 | sut.UpdateTradeAndBalance(trade, commission, BrokerKind.Trade, balances);
89 |
90 | // Assert
91 | trade.Should().BeEquivalentTo(
92 | new Trade
93 | {
94 | EntryQuantity = quantity,
95 | EntryPrice = ethPrice,
96 | BaseAsset = "ETH",
97 | Market = "ETHUSDT",
98 | TradeDirection = TradeDirection.Long,
99 | RiskAmount = 0M
100 | });
101 |
102 | balances["USDT"].Should().BeEquivalentTo(new AssetBalance("USDT", usdtBalance - buyCostExcludingFee - fee));
103 | balances["ETH"].Should().BeEquivalentTo(new AssetBalance("ETH", quantity));
104 | }
105 |
106 | [Test]
107 | public void UpdateTradeAndBalance_WhenShortEntryQuantityLessThanBalance_UpdatesTradeAndBalance()
108 | {
109 | // Arrange
110 | var sut = new TradeAmountUpdater();
111 | var commission = 0.01M;
112 | var ethPrice = 1000M;
113 | var quantity = 3;
114 | var ethBalance = 10M;
115 | Dictionary balances = new Dictionary
116 | {
117 | { "ETH", new AssetBalance("ETH", ethBalance) }
118 | };
119 |
120 | var trade = new Trade
121 | {
122 | EntryQuantity = quantity,
123 | EntryPrice = ethPrice,
124 | BaseAsset = "ETH",
125 | Market = "ETHUSDT",
126 | TradeDirection = TradeDirection.Short
127 | };
128 |
129 | // Act
130 | sut.UpdateTradeAndBalance(trade, commission, BrokerKind.Trade, balances);
131 |
132 | // Assert
133 | trade.Should().BeEquivalentTo(
134 | new Trade
135 | {
136 | EntryQuantity = quantity,
137 | EntryPrice = ethPrice,
138 | BaseAsset = "ETH",
139 | Market = "ETHUSDT",
140 | TradeDirection = TradeDirection.Short,
141 | RiskAmount = 0M
142 | });
143 |
144 | balances["ETH"].Should().BeEquivalentTo(new AssetBalance("ETH", ethBalance - quantity));
145 | balances["USDT"].Should().BeEquivalentTo(new AssetBalance("USDT", (quantity * ethPrice) - (quantity * commission * ethPrice)));
146 | }
147 |
148 | [Test]
149 | public void UpdateTradeAndBalance_WhenShortEntryQuantityMoreThanBalance_UpdatesTradeAndBalance()
150 | {
151 | // Arrange
152 | var sut = new TradeAmountUpdater();
153 | var commission = 0.01M;
154 | var ethPrice = 1000M;
155 | var ethBalance = 10M;
156 | Dictionary balances = new Dictionary
157 | {
158 | { "ETH", new AssetBalance("ETH", ethBalance) }
159 | };
160 |
161 | var trade = new Trade
162 | {
163 | EntryQuantity = 15,
164 | EntryPrice = ethPrice,
165 | BaseAsset = "ETH",
166 | Market = "ETHUSDT",
167 | TradeDirection = TradeDirection.Short
168 | };
169 |
170 | // Act
171 | sut.UpdateTradeAndBalance(trade, commission, BrokerKind.Trade, balances);
172 |
173 | // Assert
174 | trade.Should().BeEquivalentTo(
175 | new Trade
176 | {
177 | EntryQuantity = 10M,
178 | EntryPrice = ethPrice,
179 | BaseAsset = "ETH",
180 | Market = "ETHUSDT",
181 | TradeDirection = TradeDirection.Short,
182 | RiskAmount = 0M
183 | });
184 |
185 | balances["ETH"].Should().BeEquivalentTo(new AssetBalance("ETH", 0M));
186 | balances["USDT"].Should().BeEquivalentTo(new AssetBalance("USDT", (10M * ethPrice) - (10M * ethPrice * commission)));
187 | }
188 | }
189 | }*/
--------------------------------------------------------------------------------
/Src/TraderTools.Simulation.Test/TraderTools.Simulation.Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 |
7 | false
8 |
9 | AnyCPU;x64
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------