├── LICENSE
├── README.md
├── fivefactorAlpha.py
├── suolos.py
├── test.py
├── threefactor.py
├── touzi.py
├── touzi2.py
└── touzi3.py
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Quantitative-Investment_Learning
2 | ## 量化投资学习,多因子、三因子、五因子、索罗斯投资策略
3 | ### 说明:
4 | #### 1. fivefactorAlpha.py 多因子Alpha策略,作者: edward07t
5 | #### 2. suolos.py 索罗斯策略,布林线超低配池策略
6 | #### 3. threefactor.py 三因子策略
7 | #### 4. touzi.py 多因子打分策略
8 | #### 5. touzi2.py 多因子择股,简单持仓策略
9 | #### 6. touzi3.py 多因子择股,改进海龟交易法
10 |
--------------------------------------------------------------------------------
/fivefactorAlpha.py:
--------------------------------------------------------------------------------
1 | '''
2 | 策略名: 多因子Alpha策略
3 | 作者: edward07t
4 | '''
5 | import numpy as np
6 | import pandas as pd
7 | # 初始化函数 #######################################################################
8 | def init(context):
9 |
10 | # 上一次调仓期
11 | context.last_date = ''
12 |
13 | # 最大持股数量
14 | context.hold_max = 30
15 |
16 | # 设置调仓周期,每月倒数第一个交易日运行
17 | run_monthly(func=func_run_monthly, date_rule=-1)
18 |
19 |
20 | # 月末调仓函数 #######################################################################
21 | def func_run_monthly(context, bar_dict):
22 | # 获取上一个交易日的日期
23 | date = get_last_datetime().strftime('%Y%m%d')
24 |
25 | # 获取上个月末调仓日期
26 | context.last_date = func_get_end_date_of_last_month(date)
27 |
28 | log.info('############################## ' + str(date) + ' ###############################')
29 |
30 | # 获取所有A股股票代码
31 | # get_all_securities获取所有证券的基本信息,包括股票、基金、指数。
32 | # .index股票代码
33 | securities = list(get_all_securities('stock', date).index)
34 |
35 | # 获取pb, pe, ps财务因子为正的股票
36 | q = query(
37 | valuation.symbol,
38 | valuation.pb,
39 | valuation.ps_ttm,
40 | valuation.pe_ttm
41 | ).filter(
42 | valuation.pb > 0,
43 | valuation.ps_ttm > 0,
44 | valuation.pe_ttm > 0,
45 | valuation.symbol.in_(securities)
46 | )
47 | df = get_fundamentals(q, date)
48 | # 将获取到的股票代码存入securities
49 | securities = list(df['valuation_symbol'].values)
50 |
51 |
52 | # 计算过去一个月的股价动量、成交金额、ST信息
53 | # '1d' 日级,'is_st': 是否为ST, 返回值为0或1, 0表示非ST,'close': 收盘价(元),'turnover': 成交额(元)
54 | # skip_paused 是否跳过停牌数据,fq 复权选项,is_panel 返回数据格式 默认为dict
55 | # get_price(symbol_list, start_date, end_date, fre_step, fields, skip_paused = False, fq = 'pre', bar_count = 0, is_panel = 0)
56 | values = get_price(securities, context.last_date, date, '1d', ['close','turnover','is_st'], skip_paused = False, fq = 'pre', is_panel = 0)
57 |
58 | momentum = []
59 | turnover = []
60 | st = []
61 | for stock in securities:
62 | try:
63 | # 计算股价动量
64 | momentum.append((values[stock]['close'][-1] - values[stock]['close'][0]) / values[stock]['close'][0])
65 |
66 | # 计算总成交金额
67 | turnover.append(values[stock]['turnover'].sum())
68 |
69 | # 最近股票是否为ST
70 | st.append(values[stock]['is_st'][-1])
71 | except:
72 | log.info('数据缺失: %s' % stock)
73 |
74 | # 数据获取不到设置为none
75 | momentum.append(None)
76 | turnover.append(None)
77 | st.append(None)
78 |
79 | df['momentum'] = np.array(momentum)
80 | df['turnover'] = np.array(turnover)
81 | df['is_st'] = np.array(st)
82 |
83 | # 去掉ST和成交金额为0的股票
84 | df[df['is_st'] == 1] = None
85 | df[df['turnover'] == 0] = None
86 | # 过滤缺失数据
87 | df = df.dropna()
88 |
89 |
90 | # 去极值
91 | # 使用浅拷贝
92 | df = winsorize(df, 'valuation_pb', 20).copy()
93 | df = winsorize(df, 'valuation_ps_ttm', 20).copy()
94 | df = winsorize(df, 'valuation_pe_ttm', 20).copy()
95 | df = winsorize(df, 'momentum', 20).copy()
96 | df = winsorize(df, 'turnover', 20).copy()
97 | df = df.dropna()
98 |
99 |
100 | # 为全部A股打分,综合得分越小越好
101 | df['scores'] = 0
102 | # sort_values True升序排序
103 | list_pb = list(df.sort_values(['valuation_pb'], ascending = True)['valuation_symbol'].values)
104 | func_scores(df, list_pb)
105 | list_ps = list(df.sort_values(['valuation_ps_ttm'], ascending = True)['valuation_symbol'].values)
106 | func_scores(df, list_ps)
107 | list_pe = list(df.sort_values(['valuation_pe_ttm'], ascending = True)['valuation_symbol'].values)
108 | func_scores(df, list_pe)
109 | list_mo = list(df.sort_values(['momentum'], ascending = True)['valuation_symbol'].values)
110 | func_scores(df, list_mo)
111 | list_to = list(df.sort_values(['turnover'], ascending = True)['valuation_symbol'].values)
112 | func_scores(df, list_to)
113 |
114 |
115 | # 根据股票综合得分为股票排序
116 | context.selected = list(df.sort_values(['scores'], ascending = True)['valuation_symbol'].values)
117 |
118 | # 买入挑选的股票
119 | func_do_trade(context, bar_dict)
120 |
121 | context.last_date = date
122 |
123 |
124 | #### 每日检查止损条件
125 | def handle_bar(context, bar_dict):
126 |
127 | last_date = get_last_datetime().strftime('%Y%m%d')
128 | if last_date != context.last_date and len(list(context.portfolio.stock_account.positions.keys())) > 0:
129 | # 如果不是调仓日且有持仓,判断止损条件
130 | func_stop_loss(context, bar_dict)
131 |
132 |
133 | #### 1. 获取上月月末日期 #####################################################
134 | def func_get_end_date_of_last_month(current_date):
135 | # 获取从上一个交易日前一个月中的所有交易日,日期排序从前至后
136 | trade_days = list(get_trade_days(None, current_date, count=30))
137 |
138 | # 转化为%Y%m%d格式
139 | for i in range(len(trade_days)):
140 | trade_days[i] = trade_days[i].strftime('%Y%m%d')
141 |
142 | # 只要交易日的date和当前交易日的月份不同即为上一个月月末日期,例如[20171013]-[20170928]
143 | # reversed反转序列,便于快速找到月末日期
144 | for date in reversed(trade_days):
145 | if date[5] != current_date[5]:
146 | return date
147 |
148 | log.info('Cannot find the end date of last month.')
149 | return
150 |
151 |
152 | #### 2. 中位数去极值函数 ####################################################
153 | def winsorize(df, factor, n=20):
154 | '''
155 | df为bar_dictFrame数据
156 | factor为需要去极值的列名称
157 | n 为判断极值上下边界的常数
158 | '''
159 | ls_raw = np.array(df[factor].values)
160 | # 排序 axis=0,按列排列
161 | ls_raw.sort(axis = 0)
162 | # 获取中位数
163 | D_M = np.median(ls_raw)
164 |
165 | # 计算离差值
166 | ls_deviation = abs(ls_raw - D_M)
167 | ls_deviation.sort(axis = 0)
168 | # 获取离差中位数
169 | D_MAD = np.median(ls_deviation)
170 |
171 | # 将大于中位数n倍离差中位数的值赋为NaN
172 | df.loc[df[factor] >= D_M + n * D_MAD, factor] = None
173 | # 将小于中位数n倍离差中位数的值赋为NaN
174 | df.loc[df[factor] <= D_M - n * D_MAD, factor] = None
175 |
176 | return df
177 |
178 |
179 | #### 3. 按因子排序打分函数 #############################################################
180 | def func_scores(df, ls):
181 | '''
182 | 按照因子暴露值将股票分为20档
183 | 第一档股票综合得分+1分
184 | 第二档股票综合得分+2分
185 | 以此类推
186 | '''
187 | # 每档有quotient只股票 //整除,向下取整
188 | quotient = len(ls) // 20
189 | # 余数
190 | remainder = len(ls) % 20
191 | layer = np.array([quotient]*20)
192 |
193 | for i in range(0, remainder):
194 | layer[-(1+i)] += 1
195 |
196 | layer = np.insert(layer, 0, 0)
197 | layer = layer.cumsum()
198 |
199 | for i in range(0,20):
200 | for j in range(layer[i], layer[i+1]):
201 | df.loc[df['valuation_symbol'] == ls[j], 'scores'] += (i + 1)
202 |
203 |
204 | #### 4.下单函数 ###################################################################
205 | def func_do_trade(context, bar_dict):
206 | # 先清空所有持仓
207 | if len(list(context.portfolio.stock_account.positions.keys())) > 0:
208 | for stock in list(context.portfolio.stock_account.positions.keys()):
209 | order_target(stock, 0)
210 |
211 | # 买入前30支股票
212 | for stock in context.selected:
213 | order_target_percent(stock, 1./context.hold_max)
214 | if len(list(context.portfolio.stock_account.positions.keys())) >= context.hold_max:
215 | break
216 | return
217 |
218 |
219 | #### 5.止损函数 ####################################################################
220 | def func_stop_loss(context, bar_dict):
221 | #获取账户持仓信息
222 | holdstock = list(context.portfolio.stock_account.positions.keys())
223 | if len(holdstock) > 0:
224 | num = -0.1
225 | for stock in holdstock:
226 | close = history(stock,['close'],1,'1d').values
227 | if close/context.portfolio.positions[stock].last_price -1 <= num:
228 | order_target(stock,0)
229 | log.info('股票{}已止损'.format(stock))
230 |
231 |
232 | #获取账户持仓信息
233 | holdstock = list(context.portfolio.stock_account.positions.keys())
234 | if len(holdstock) > 0:
235 | num = - 0.13
236 | T = history('000001.SH',['quote_rate'],7,'1d').values.sum()
237 | if T < num*100:
238 | log.info('上证指数连续三天下跌{}已清仓'.format(T))
239 | for stock in holdstock:
240 | order_target(stock,0)
241 |
--------------------------------------------------------------------------------
/suolos.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import numpy as np
3 | import datetime, time
4 | import statsmodels.api as sm
5 | import talib
6 | import math
7 |
8 | def init(context):
9 | set_log_level('warn')
10 | #智能选股
11 | get_iwencai('沪深 300')
12 | #设置交易周期
13 | context.pp=5
14 | #设置账户总资金
15 | context.cash=10000000
16 | #设置最大持股数
17 | context.max_stocks=10
18 | #设置筛选因子个数
19 | context.max_need=5
20 | #设置因子测试期数
21 | context.fpn=9
22 | context.st=0
23 | #设置检验因子所用的股票数
24 | context.samt=5
25 | #设置子账户
26 | set_subportfolios([{'cash':7000000,'type':'stock'},{'cash':3000000,'type':'future'}])
27 | log.warn(['context.cash:',context.cash])
28 | #设置计数起点
29 | context.cday=0
30 | #设置因子池
31 | context.need=['factor.pe',
32 | 'factor.pb',
33 | 'factor.pcf_cash_flow_ttm',
34 | 'factor.ps',
35 | 'factor.dividend_rate',
36 | 'factor.market_cap',
37 | 'factor.current_market_cap',
38 | 'factor.capitalization',
39 | 'factor.circulating_cap',
40 | 'factor.current_ratio',
41 | 'factor.equity_ratio',
42 | 'factor.quick_ratio',
43 | 'factor.tangible_assets_liabilities',
44 | 'factor.tangible_assets_int_liabilities',
45 | 'factor.net_debt_equity',
46 | 'factor.long_term_debt_to_opt_capital_ratio',
47 | 'factor.tangible_assets_net_liabilities',
48 | 'factor.overall_income_growth_ratio',
49 | 'factor.net_cashflow_psg_rowth_ratio',
50 | 'factor.opt_profit_grow_ratio',
51 | 'factor.total_profit_growth_ratio',
52 | 'factor.diluted_net_asset_growth_ratio',
53 | 'factor.net_cashflow_from_opt_act_growth_ratio',
54 | 'factor.net_profit_growth_ratio',
55 | 'factor.basic_pey_ear_growth_ratio',
56 | 'factor.turnover_of_overall_assets',
57 | 'factor.turnover_ratio_of_context_payable',
58 | 'factor.turnover_of_current_assets',
59 | 'factor.turnover_of_fixed_assets',
60 | 'factor.cash_cycle',
61 | 'factor.inventory_turnover_ratio',
62 | 'factor.turnover_ratio_of_receivable',
63 | 'factor.weighted_roe',
64 | 'factor.overall_assets_net_income_ratio',
65 | 'factor.net_profit_margin_on_sales',
66 | 'factor.before_tax_profit_div_income',
67 | 'factor.sale_cost_div_income',
68 | 'factor.roa',
69 | 'factor.ratio_of_sales_to_cost',
70 | 'factor.net_profit_div_income',
71 | 'factor.opt_profit_div_income',
72 | 'factor.opt_cost_div_income',
73 | 'factor.administration_cost_div_income',
74 | 'factor.financing_cost_div_income',
75 | 'factor.vr_rate',
76 | 'factor.vstd',
77 | 'factor.arbr',
78 | 'factor.srdm',
79 | 'factor.vroc',
80 | 'factor.vrsi',
81 | 'factor.cr',
82 | 'factor.mfi',
83 | 'factor.vr',
84 | 'factor.mass',
85 | 'factor.obv',
86 | 'factor.pvt',
87 | 'factor.wad',
88 | 'factor.bbi',
89 | 'factor.mtm',
90 | 'factor.dma',
91 | 'factor.ma',
92 | 'factor.macd',
93 | 'factor.expma',
94 | 'factor.priceosc',
95 | 'factor.trix',
96 | 'factor.dbcd',
97 | 'factor.dpo',
98 | 'factor.psy',
99 | 'factor.vma',
100 | 'factor.vmacd',
101 | 'factor.vosc',
102 | 'factor.tapi',
103 | 'factor.micd',
104 | 'factor.rccd']
105 | #设置股票因子及其方向
106 | context.mfactors={'factor.pe':-1,
107 | 'factor.pb':-1,
108 | 'factor.pcf_cash_flow_ttm':-1,
109 | 'factor.ps':-1,
110 | 'factor.dividend_rate':1,
111 | 'factor.market_cap':-1,
112 | 'factor.current_market_cap':-1,
113 | 'factor.capitalization':-1,
114 | 'factor.circulating_cap':-1,
115 | 'factor.current_ratio':1,
116 | 'factor.equity_ratio':-1,
117 | 'factor.quick_ratio':1,
118 | 'factor.tangible_assets_liabilities':1,
119 | 'factor.tangible_assets_int_liabilities':1,
120 | 'factor.net_debt_equity':-1,
121 | 'factor.long_term_debt_to_opt_capital_ratio':-1,
122 | 'factor.tangible_assets_net_liabilities':1,
123 | 'factor.overall_income_growth_ratio':1,
124 | 'factor.net_cashflow_psg_rowth_ratio':1,
125 | 'factor.opt_profit_grow_ratio':1,
126 | 'factor.total_profit_growth_ratio':1,
127 | 'factor.diluted_net_asset_growth_ratio':1,
128 | 'factor.net_cashflow_from_opt_act_growth_ratio':1,
129 | 'factor.net_profit_growth_ratio':1,
130 | 'factor.basic_pey_ear_growth_ratio':1,
131 | 'factor.turnover_of_overall_assets':1,
132 | 'factor.turnover_ratio_of_context_payable':1,
133 | 'factor.turnover_of_current_assets':1,
134 | 'factor.turnover_of_fixed_assets':1,
135 | 'factor.cash_cycle':-1,
136 | 'factor.inventory_turnover_ratio':1,
137 | 'factor.turnover_ratio_of_receivable':1,
138 | 'factor.weighted_roe':1,
139 | 'factor.overall_assets_net_income_ratio':1,
140 | 'factor.net_profit_margin_on_sales':1,
141 | 'factor.before_tax_profit_div_income':1,
142 | 'factor.sale_cost_div_income':-1,
143 | 'factor.roa':1,
144 | 'factor.ratio_of_sales_to_cost':-1,
145 | 'factor.net_profit_div_income':1,
146 | 'factor.opt_profit_div_income':1,
147 | 'factor.opt_cost_div_income':-1,
148 | 'factor.administration_cost_div_income':-1,
149 | 'factor.financing_cost_div_income':-1,
150 | 'factor.vr_rate':1,
151 | 'factor.vstd':-1,
152 | 'factor.arbr':1,
153 | 'factor.srdm':1,
154 | 'factor.vroc':1,
155 | 'factor.vrsi':1,
156 | 'factor.cr':1,
157 | 'factor.mfi':1,
158 | 'factor.vr':1,
159 | 'factor.mass':1,
160 | 'factor.obv':1,
161 | 'factor.pvt':1,
162 | 'factor.wad':1,
163 | 'factor.bbi':1,
164 | 'factor.mtm':1,
165 | 'factor.dma':1,
166 | 'factor.ma':1,
167 | 'factor.macd':1,
168 | 'factor.expma':1,
169 | 'factor.priceosc':1,
170 | 'factor.trix':1,
171 | 'factor.dbcd':1,
172 | 'factor.dpo':1,
173 | 'factor.psy':1,
174 | 'factor.vma':1,
175 | 'factor.vmacd':1,
176 | 'factor.vosc':1,
177 | 'factor.tapi':1,
178 | 'factor.micd':1,
179 | 'factor.rccd':1}
180 | context.lastlong={}.fromkeys(context.need) #超配股票池
181 | context.lastshort={}.fromkeys(context.need) #低配股票池
182 | context.winr={}
183 | context.ir={}
184 | context.incomer={}
185 | #设置默认因子
186 | context.realneed=['factor.pe','factor.weighted_roe','factor.net_cashflow_from_opt_act_growth_ratio',
187 | 'factor.overall_income_growth_ratio','factor.turnover_ratio_of_context_payable']
188 | #设置打分机制
189 | context.score=5
190 | #定期运行函数
191 | run_monthly(func=reallocate,date_rule=8)
192 |
193 | def reallocate(context,bar_dict):
194 | log.warn('……每月第 8 交易日/起始日……')
195 | tempstocks=give_me_stocks(context,bar_dict,is_re=1)
196 | #前maxstocks只股票带综合分数
197 | #log.info('This is the stocks for choosing')
198 | #log.info(tempstocks)
199 | nstocks=[]
200 | for i in range(len(tempstocks)):
201 | nstocks.append(tempstocks[i][0])
202 | #起手均分现金,之后均分持仓规模
203 | log.warn('刷新持仓股票')
204 | if(context.cday==0):
205 | cash=context.cash/context.max_stocks
206 | log.warn(['context.cash:',context.cash])
207 | else:
208 | # 分配可用资金
209 | cash=context.portfolio.portfolio_value/context.max_stocks
210 | # 原股票池中的股票不在新股票池中则清0
211 | for i in list(context.portfolio.positions.keys()):
212 | if(i not in nstocks[:context.max_stocks]):
213 | order_target(i,0)
214 | for i in nstocks:
215 | order_target_value(i,cash)
216 | if(len(context.portfolio.positions.keys())==context.max_stocks):
217 | break
218 | log.warn('刷新完毕')
219 | log.warn('期货操作')
220 | Astocks=[] #用于计算期货份数的列表,存放持股分数
221 | tstocks=dict(tempstocks)
222 | ctr=0
223 | for i in list(context.portfolio.positions.keys()):
224 | if i not in tstocks:
225 | continue
226 | Astocks.append((i,tstocks[i]))
227 | ctr+=1
228 | #log.info(ctr)
229 | Amount=give_me_amount(context,bar_dict,*Astocks)
230 | #log.info(['The number of Amount is',Amount])
231 | code=get_future_code('IF','next_month') #下月期货
232 | if(len(context.portfolio.stock_account.positions)==0): #若子账户期货为空,说明当月期货已经交割,入手下月
233 | order(code,Amount,pindex=1,type='short')
234 | log.warn('下月期货入手')
235 | else:
236 | current_code=list(context.portfolio.stock_account.positions)[0] #手头期货
237 | number=context.portfolio.stock_account.positions[current_code].total_amount #手头期货份数
238 | if(code==current_code): #如果手头的是下月期货,直接调仓
239 | if Amount>number:
240 | order(current_code,-number+Amount,pindex=1,type='short') #做空期货,卖出
241 | log.warn('做空期货')
242 | else:
243 | order(current_code,number-Amount,pindex=1,type='long') #做多期货,买回
244 | log.warn('做多期货')
245 | else:
246 | order(current_code,-number,pindex=1,type='long') #平仓买回
247 | order(code,Amount,pindex=1,type='short') #做空下月期货
248 | log.warn('平仓买回 AND 做空下月期货')
249 | log.warn('期货操作完毕')
250 |
251 | def give_me_amount(context,bar_dict,*tempstocks):
252 | log.warn('获取期货买卖份数')
253 | #计算 Beta 值
254 | beta=[]
255 | percent=[]
256 | nstocks=[]
257 | rstocks=[]
258 | sum1=0.0
259 | for i in range(len(tempstocks)):
260 | sum1+=tempstocks[i][1]
261 | rstocks.append(tempstocks[i][1])
262 | nstocks.append(tempstocks[i][0])
263 | #计算权重
264 | for i in range(len(rstocks)):
265 | percent.append(rstocks[i]/sum1)
266 | for i in nstocks: #迭代股票池
267 | iclose=get_price(['000300.SH',i],None,get_datetime().strftime('%Y%m%d'),'1d',['close'],False,None,220) #证券
268 | xclose=iclose['000300.SH']['close']
269 | yclose=iclose[i]['close']
270 | if(len(yclose)==0):
271 | #log.info('糟糕,yclose 长度是 0')
272 | beta.append(0)
273 | continue
274 | if(len(xclose)!=len(yclose)):
275 | #log.info(['iclose 是',iclose])
276 | l=min(len(xclose),len(yclose))
277 | xclose=xclose[-l:]
278 | yclose=yclose[-l:]
279 | x=np.array(xclose)
280 | y=np.array(yclose)
281 | rm=(x[1:]-x[0:-1])/x[0:-1]
282 | r1=(y[1:]-y[0:-1])/y[0:-1]
283 | est=sm.OLS(r1,sm.add_constant(rm))
284 | est=est.fit() #单只股票的 beta
285 | beta.append(est.params[1])
286 | Beta=0.0
287 | for i in range(len(beta)):
288 | Beta+=beta[i]*percent[i] #Beat 等于因子打分权重之和
289 | #log.info(['Beta 是',Beta])
290 | close=history('000300.SH',['close'],1,'1d') #期货开盘价
291 | #log.info(['沪深 300 股指期货的开盘价是',close])
292 | cash=context.portfolio.portfolio_value*1.0/context.max_stocks
293 | log.warn(['Beta and cash and 期货价:',Beta,cash,close['close'][0]])
294 | Amount=math.ceil((Beta*(cash*context.max_stocks))/(300*close['close'][0]))
295 | log.warn(['Amount 是',Amount])
296 | log.warn('期货买卖份数获取完毕')
297 | return int(Amount*1)
298 |
299 | def give_me_need(context,bar_dict):
300 | log.warn('获取新建因子')
301 | n=len(context.mfactors)
302 | querc=','.join(context.need)
303 | #log.info(querc)
304 | q=query(querc).filter(factor.symbol.in_(context.iwencai_securities),factor.date==get_datetime().strftime('%Y-%m-%d'))
305 | df=get_factors(q).fillna(0)
306 | #log.info(df)
307 | df['factor_symbol']=context.iwencai_securities[:len(df)]
308 | n=len(df)
309 | m=len(df.columns)-1
310 | for k in range(m):
311 | tempdf=df.sort_values(df.columns[k])
312 | name=list(tempdf['factor_symbol'])
313 | #log.info('排序后的 df')
314 | #log.info(tempdf)
315 | #log.info(name)
316 | f='factor.'+df.columns[k]
317 | #log.info(f)
318 | if context.mfactors[f]<0:
319 | context.lastlong[f]=name[:context.samt*2]
320 | context.lastshort[f]=name[-2*context.samt:]
321 | else:
322 | context.lastlong[f]=name[-2*context.samt:]
323 | context.lastshort[f]=name[:2*context.samt]
324 | #log.info(['long',len(context.lastlong[f])])
325 | #log.info(['short',len(context.lastshort[f])])
326 | value=get_price(context.lastlong[f],None,get_datetime().strftime("%Y%m%d"),'22d',['close'],True,None,context.fpn)
327 | rsum1=np.zeros((context.fpn-1,1))
328 | ctr=0
329 | for stk in context.lastlong[f]:
330 | #log.info(['这是 value[stk]',value[stk]])
331 | x=np.array(value[stk])
332 | #log.info(x)
333 | if(len(x)!=context.fpn):
334 | #log.info('超配之力不从心')
335 | continue
336 | if(ctr==context.samt):
337 | break
338 | rsum1+=(x[1:]-x[:-1])/x[:-1]
339 | ctr=ctr+1
340 | #log.info([rsum1,ctr])
341 | value=get_price(context.lastshort[f],None,get_datetime().strftime("%Y%m%d"),'22d',['close'],True,None,context.fpn)
342 | rsum2=np.zeros((context.fpn-1,1))
343 | ctr=0
344 | for stk in context.lastshort[f]:
345 | x=np.array(value[stk])
346 | #log.info(x)
347 | if(len(x)!=context.fpn):
348 | #log.info('低配之力不从心')
349 | continue
350 | if(ctr==context.samt):
351 | break
352 | rsum2+=(x[1:]-x[:-1])/x[:-1]
353 | ctr=ctr+1
354 | #log.info([rsum2,ctr])
355 | context.winr[f]=((np.sum(rsum1-rsum2>0)/(context.fpn-1)))
356 | context.ir[f]=(np.mean(rsum1-rsum2)/np.std(rsum1-rsum2))
357 | context.incomer[f]=(np.mean(rsum1))
358 | #log.info(context.winr)
359 | #log.info(context.ir)
360 | #log.info(context.winr)
361 | #log.info(context.ir)
362 | #log.info(context.incomer)
363 | score={}
364 | score=score.fromkeys(list(context.need),0.0)
365 | #log.info(score)
366 | temp=sorted(context.winr.items(),key=lambda item:item[1])
367 | for i in range(len(temp)):
368 | score[temp[i][0]]+=0.4*i
369 | temp=sorted(context.ir.items(),key=lambda item:item[1])
370 | for i in range(len(temp)):
371 | score[temp[i][0]]+=0.4*i
372 | #log.info(score)
373 | temp=sorted(context.incomer.items(),key=lambda item:item[1])
374 | for i in range(len(temp)):
375 | score[temp[i][0]]+=0.2*i
376 | #log.info(score)
377 | tempscore=sorted(score.items(),key=lambda item:item[1],reverse=True)
378 | #log.info(tempscore)
379 | realneed=[]
380 | for i in range(context.max_need):
381 | realneed.append(tempscore[i][0])
382 | log.info(realneed)
383 | log.warn('因子获取完毕')
384 | return realneed
385 |
386 | def give_me_stocks(context,bar_dict,is_re):
387 | log.warn('获取新建股票池')
388 | samp=context.iwencai_securities
389 | if(is_re==1):
390 | context.realneed=give_me_need(context,bar_dict)
391 | #从数据库查询因子,获取 q
392 | #log.info(context.realneed)
393 | need=','.join(context.realneed)
394 | #log.info(['这是 need',need])
395 | q=query(need).filter(factor.symbol.in_(samp),factor.date==get_datetime().strftime('%Y-%m-%d') )
396 | df=get_factors(q).fillna(0) #结合当天时间,形成 dateframe,缺失值填充为 0
397 | samp=samp[:len(df)]
398 | df['factor_symbol']=samp
399 | n=len(df) #获取行数,即总共获取的股票数目
400 | m=len(df.columns)-1 #获取列数(因子数目)=总列数-1
401 | stocks={}
402 | stocks=stocks.fromkeys(samp,0.0) #预设打分股票池
403 | for i in range(m): #先对因子一列从小到大进行排序,方便打分 ,逐列展开
404 | f='factor.'+df.columns[i]
405 | if(context.mfactors[f]<0):
406 | tempdf=df.sort_values(df.columns[i]) #从小到大
407 | else:
408 | tempdf=df.sort_values(df.columns[i],ascending=False)
409 | security=list(tempdf['factor_symbol']) #排序后的股票代码,重新构建序列
410 | for j in range(n): #确定 n,即确定的股票代码
411 | name=security[j]#字符串格式,股票的代码
412 | rank=int((j/(n/context.score))) #确定等级
413 | stocks[name]+=(5-rank)
414 | tempstocks=sorted(stocks.items(),key=lambda item:item[1],reverse=True) #给已经拥有分数的股票池从大到小排序
415 | log.info(tempstocks)
416 | log.warn('新建股票池获取完毕')
417 | return tempstocks[:2*context.max_stocks]
418 |
419 | def boll(context,bar_dict):
420 | log.warn('布林线检测')
421 | flag=0 #设置触发变量
422 | up=[[],[]]
423 | mid=[[],[]]
424 | low=[[],[]]
425 | cprice=[[],[]]
426 | last=-6
427 | now=-1
428 | for i in list(context.portfolio.positions.keys()):
429 | # 获取历史收盘价
430 | values=history(i,['close'],21,'1d',False,None).fillna(0)
431 | upper,middle,lower=talib.BBANDS(values['close'].values,timeperiod=15,nbdevup=2,nbdevdn=2,matype=0)
432 | up[0].append(upper[last])
433 | up[1].append(upper[now])
434 | mid[0].append(middle[last])
435 | mid[1].append(middle[now])
436 | low[0].append(lower[last])
437 | low[1].append(lower[now])
438 | cprice[0].append(values['close'][last])
439 | cprice[1].append(values['close'][now])
440 | up=np.mean(up,1)
441 | mid=np.mean(mid,1)
442 | low=np.mean(low,1)
443 | cprice=np.mean(cprice,1)
444 | #log.info(up)
445 | if(up[1]>up[0] and low[1]mid[1]):
447 | flag=1
448 | log.warn('开轨')
449 | elif(up[1]low[0]): #收轨
450 | if((cprice[0]up[1]) or (cprice[0]>low[0] and
451 | cprice[1]0): #三轨同向
455 | if(cprice[0]>mid[0] and cprice[1]=context.cday): #如果超过计时点,就进行判断
471 | if(context.cday==0):
472 | reallocate(context,bar_dict) #月初处理
473 | context.cday+=context.pp #调整计时起点
474 | else: #非初始日
475 | #布林线通道
476 | log.warn([context.pp,'/天一检测'])
477 | if(boll(context,bar_dict)==1 or int(dd.days)%(15)==0): #布林线判断出要构建新的股票池或者距离上一次构建达到达到 15 天
478 | if(int(dd.days)%(15)==0):
479 | log.warn('15 天/构建')
480 | tempstocks=give_me_stocks(context,bar_dict,is_re=0)
481 | nstocks=[]
482 | for i in range(len(tempstocks)):
483 | nstocks.append(tempstocks[i][0])
484 | log.info(nstocks)
485 | cash=context.portfolio.portfolio_value/context.max_stocks
486 | log.warn('刷新持仓股票')
487 | ctr=0
488 | for i in list(context.portfolio.positions.keys()):
489 | if(i not in nstocks[:context.max_stocks]):#股票不存在现有账户中,此回合需卖出
490 | order_target(i,0) #全部卖出
491 | ctr+=1
492 | log.warn(['卖出股票数:',ctr])
493 | for i in nstocks:
494 | order_target_value(i,cash) #调整股票使满足目标金额
495 | if(len(context.portfolio.positions.keys())==context.max_stocks):
496 | break
497 | log.warn('进行期货操作')
498 | Astocks=[]
499 | tstocks=dict(tempstocks)
500 | for i in list(context.portfolio.positions.keys()):
501 | if i not in tstocks:
502 | continue
503 | Astocks.append((i,tstocks[i]))
504 | Amount=give_me_amount(context,bar_dict,*Astocks)
505 | if(len(context.portfolio.stock_account.positions)==0): #若子账户期货为空,说明当月期货已经提前交割,入手下月
506 | code=get_future_code('IF','next_month')
507 | order(code,Amount,pindex=1,type='short')
508 | log.warn('下月期货入手')
509 | else:
510 | current_code=list(context.portfolio.stock_account.positions)[0] #手头期货
511 | number=context.portfolio.stock_account.positions[current_code].total_amount
512 | if Amount>number:
513 | log.warn('做空期货')
514 | order(current_code,-number+Amount,pindex=1,type='short')
515 | else:
516 | log.warn('做多期货')
517 | order(current_code,number-Amount,pindex=1,type='long')
518 | context.cday+=context.pp #调整计时起点
519 | log.warn([context.pp,'/天一检测(15 天检测)完毕'])
520 |
521 |
--------------------------------------------------------------------------------
/test.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import numpy as np
3 | import datetime, time
4 | from dateutil.relativedelta import relativedelta
5 | import talib
6 | import math
7 | from scipy.stats import ttest_ind
8 | from scipy.stats import levene
9 | from sklearn.linear_model import LinearRegression
10 |
11 | def init(context):
12 |
13 | # 最大持股数量
14 | context.hold_max = 40
15 |
16 | # 初始因子池(包括技术因子和财务因子在内的共计107个初始因子)
17 | context.need=['factor.pe',# 市盈率
18 | 'factor.pb',# 市净率
19 | 'factor.pcf_cash_flow_ttm',# 市现率PCF
20 | 'factor.ps',# 市销率
21 | 'factor.dividend_rate',# 股息率
22 | 'factor.market_cap',# 总市值
23 | 'factor.current_market_cap',# 流通市值
24 | 'factor.capitalization',# 总股本
25 | 'factor.circulating_cap',# 流通股本
26 | 'factor.current_ratio',# 流动比率
27 | 'factor.equity_ratio',# 产权比率负债合计/归属母公司股东的权益
28 | 'factor.quick_ratio',# 速动比率
29 | 'factor.tangible_assets_liabilities',# 有形资产/负债合计
30 | 'factor.tangible_assets_int_liabilities',# 有形资产/带息债务
31 | 'factor.net_debt_equity',# 净债务/股权价值
32 | 'factor.long_term_debt_to_opt_capital_ratio',# 长期债务与营运资金比率
33 | 'factor.tangible_assets_net_liabilities',# 有形资产/净债务
34 | 'factor.overall_income_growth_ratio',# 营业总收入同比增长率
35 | # 'factor.net_cashflow_psg_rowth_ratio',# 每股经营活动产生的现金流量净额同比增长率(报错,原因询问客服ing)
36 | # 'factor.opt_profit_grow_ratio',# 营业利润同比增长率
37 | # 'factor.total_profit_growth_ratio',# 利润总额同比增长率
38 | 'factor.diluted_net_asset_growth_ratio',# 净资产收益率摊薄同比增长率
39 | 'factor.net_cashflow_from_opt_act_growth_ratio',# 经营活动产生的现金流量净额同比增长率
40 | 'factor.net_profit_growth_ratio',# 净利润同比增长率
41 | # 'factor.basic_pey_ear_growth_ratio',# 基本每股收益同比增长率
42 | 'factor.turnover_of_overall_assets',# 总资产周转率
43 | 'factor.turnover_ratio_of_account_payable',# 应付账款周转率
44 | 'factor.turnover_of_current_assets',# 流动资产周转率
45 | 'factor.turnover_of_fixed_assets',# 固定资产周转率
46 | 'factor.cash_cycle',# 现金循环周期
47 | 'factor.inventory_turnover_ratio',# 存货周转率
48 | 'factor.turnover_ratio_of_receivable',# 应收账款周转率
49 | 'factor.weighted_roe',# 净资产收益率roe加权
50 | 'factor.overall_assets_net_income_ratio',# 总资产净利率roa
51 | 'factor.net_profit_margin_on_sales',# 销售净利率
52 | 'factor.before_tax_profit_div_income',# 息税前利润/营业总收入
53 | 'factor.sale_cost_div_income',# 销售费用/营业总收入
54 | 'factor.roa',# 总资产报酬率roa
55 | 'factor.ratio_of_sales_to_cost',# 销售成本率
56 | 'factor.net_profit_div_income',# 净利润/营业总收入
57 | 'factor.opt_profit_div_income',# 营业利润/营业总收入
58 | 'factor.opt_cost_div_income',# 营业总成本/营业总收入
59 | 'factor.administration_cost_div_income',# 管理费用/营业总收入
60 | 'factor.financing_cost_div_income',# 财务费用/营业总收入
61 | 'factor.impairment_loss_div_income',# 资产减值损失/营业总收
62 | # 以下为技术因子
63 | 'factor.vr_rate',# 成交量比率
64 | 'factor.vstd',# 成交量标准差
65 | 'factor.arbr',# 人气意愿指标
66 | 'factor.srdm',# 动向速度比率
67 | 'factor.vroc',# 量变动速率
68 | 'factor.vrsi',# 量相对强弱指标
69 | 'factor.cr',# 能量指标
70 | 'factor.mfi',# 资金流向指标
71 | 'factor.vr',# 量比
72 | 'factor.mass',# 梅丝线
73 | 'factor.obv',# 能量潮
74 | 'factor.pvt',# 量价趋势指标
75 | 'factor.wad',# 威廉聚散指标
76 | 'factor.bbi',# 多空指数
77 | 'factor.mtm',# 动力指标
78 | 'factor.dma',# 平均线差
79 | 'factor.ma',# 简单移动平均
80 | 'factor.macd',# 指数平滑异同平均
81 | 'factor.expma',# 指数平均数
82 | 'factor.priceosc',# 价格振荡指标
83 | 'factor.trix',# 三重指数平滑平均
84 | 'factor.dbcd',# 异同离差乖离率
85 | 'factor.dpo',# 区间震荡线
86 | 'factor.psy',# 心理指标
87 | 'factor.vma',# 量简单移动平均
88 | 'factor.vmacd',# 量指数平滑异同平均
89 | 'factor.vosc',# 成交量震荡
90 | 'factor.tapi',# 加权指数成交值
91 | 'factor.micd',# 异同离差动力指数
92 | 'factor.rccd',# 异同离差变化率指数
93 | 'factor.ddi',# 方向标准差偏离指数
94 | 'factor.bias',# 乖离率
95 | 'factor.cci',# 顺势指标
96 | 'factor.kdj',# 随机指标
97 | 'factor.lwr',# L威廉指标
98 | 'factor.roc',# 变动速率
99 | 'factor.rsi',# 相对强弱指标
100 | 'factor.si',# 摆动指标
101 | 'factor.wr',# 威廉指标
102 | 'factor.wvad',# 威廉变异离散量
103 | 'factor.bbiboll',# BBI多空布林线
104 | 'factor.cdp',# 逆势操作
105 | 'factor.env',# ENV指标
106 | 'factor.mike',# 麦克指标
107 | 'factor.adtm',# 动态买卖气指标
108 | 'factor.mi',# 动量指标
109 | 'factor.rc',# 变化率指数
110 | 'factor.srmi',# SRMIMI修正指标
111 | 'factor.dptb',# 大盘同步指标
112 | 'factor.jdqs',# 阶段强势指标
113 | 'factor.jdrs',# 阶段弱势指标
114 | 'factor.zdzb',# 筑底指标
115 | 'factor.atr',# 真实波幅
116 | 'factor.std',# 标准差
117 | 'factor.vhf',# 纵横指标
118 | 'factor.cvlt']# 佳庆离散指标
119 |
120 | # 最大因子数目
121 | context.need_max = 10
122 |
123 | # 上一次调仓期
124 | context.last_date = ''
125 |
126 | # 设置调仓周期,每月倒数第一个交易日运行
127 | run_monthly(func = reallocate, date_rule = -1)
128 |
129 | # 设置因子测试周期为12个月
130 | context.need_tmonth = 12
131 |
132 | # 选择沪深300
133 | get_iwencai('沪深300')
134 |
135 | def reallocate(context, bar_dict):
136 | # 获取上一个交易日的日期
137 | date = get_last_datetime().strftime('%Y%m%d')
138 | log.info('上个交易日日期为:' + date)
139 |
140 | # 获取上个月末调仓日期
141 | context.last_date = func_get_end_date_of_last_month(date)
142 |
143 | log.info('上月月末调仓日期为:' + context.last_date)
144 |
145 | f_needs = get_needs(context, bar_dict, date)
146 | log.info(f_needs)
147 |
148 | # 获得因子函数
149 | def get_needs(context, bar_dict, date):
150 | time_tuple = time.strptime(date, '%Y%m%d')
151 | year, month, day = time_tuple[:3]
152 | # 转化为datetime.date类型
153 | date = datetime.date(year, month, day)
154 |
155 | last_year_date =(date - relativedelta(years = 1)).strftime('%Y%m%d')
156 | log.info('上个交易日前一年的日期为:' + last_year_date)
157 |
158 | # 上一年内的所有交易日期
159 | trade_days = get_trade_days(last_year_date, date).strftime('%Y-%m-%d')
160 |
161 | # 所有因子
162 | need_all = ','.join(context.need)
163 |
164 | # 1.分组单因子有效性检验,参考资料:https://www.ricequant.com/community/topic/702/%E5%8D%95%E4%B8%80%E5%9B%A0%E5%AD%90%E6%9C%89%E6%95%88%E6%80%A7%E6%A3%80%E6%B5%8B/2
165 | # 每隔25天(间隔越短,收益就会越随机)
166 | period = 25
167 |
168 | # 有效因子列表
169 | eff_need = []
170 |
171 | # 每组股票池数10%只股票
172 | num = 30
173 |
174 | # 获取开始日期因子值
175 | q = query(
176 | factor.symbol,
177 | need_all
178 | ).filter(
179 | factor.symbol.in_(context.iwencai_securities),
180 | # 20180430的数据消失,原因是:last_year_date可能不是交易日,这样就获取不到数据,因此为trade_days[0]
181 | factor.date == trade_days[0]
182 | )
183 |
184 | df = get_factors(q)
185 |
186 | for col in df.columns.values[1:]:
187 | # 分别按正序和倒序提取因子值前20和后20的股票
188 | a_hign = df.sort_values(col, axis=0, ascending=False)
189 | a_low = df.sort_values(col, axis=0, ascending=True)
190 |
191 | hign = a_hign.iloc[0:num, 0].values.tolist()
192 | low = a_low.iloc[0:num, 0].values.tolist()
193 | hign_price = get_price(hign, last_year_date, date, '1d', ['close'], skip_paused = False, fq = 'pre', is_panel = 1)
194 | low_price = get_price(low, last_year_date, date, '1d', ['close'], skip_paused = False, fq = 'pre', is_panel = 1)
195 |
196 | hign_price = hign_price['close']
197 | low_price = low_price['close']
198 |
199 | # 计算平均每组股票每日价格
200 | high_series = hign_price.T.mean()
201 | low_series = low_price.T.mean()
202 |
203 | # 计算收益率
204 | hign_rr = high_series.pct_change(period).dropna()
205 | low_rr = low_series.pct_change(period).dropna()
206 |
207 | # 判断两组数据的方差是否显著不同
208 | le = levene(hign_rr, low_rr)
209 |
210 | # 判断两组数据的均值是否显著不同
211 | tt = ttest_ind(hign_rr, low_rr, equal_var = False)
212 |
213 | # 取置信度为0.05
214 | if le.pvalue < 0.05 and tt.pvalue < 0.05:
215 | eff_need.append('factor.%s' % col)
216 |
217 | # 第一步有效因子个数
218 | log.info(len(eff_need))
219 |
220 | effneed = ','.join(eff_need)
221 | # 第二步暂时去除
222 | # 可能出现合格因子数目很少的情况
223 | # 拟定:当第一步的因子大于最大因子数目进行第二步,小于则直接进行第三步
224 | if len(eff_need) > context.need_max:
225 | # 2.单因子单股票回归性检验
226 |
227 | # 存放各因子回归系数t检验值
228 | t_data = pd.DataFrame()
229 |
230 | for stock in context.iwencai_securities:
231 | q = query(
232 | factor.date,
233 | factor.symbol,
234 | effneed
235 | ).filter(
236 | factor.symbol == stock,
237 | factor.date.in_(trade_days)
238 | )
239 |
240 | # get_fundamentals获取财务数,get_factors获取因子数据(暂时将缺失值补充为0)
241 | df = get_factors(q).fillna(0)
242 |
243 | # 去除所有值都缺失的列
244 | df = df.ix[:,~((df==0).all())]
245 |
246 | prices = get_price(stock, last_year_date, date, '1d', ['close'], skip_paused = False, fq = 'pre',is_panel = 0)
247 | stock_close = prices['close'].values
248 | stock_rate = np.diff(stock_close) / stock_close[:-1]
249 |
250 | df = df.iloc[:-1, :]
251 | df['rate'] = stock_rate
252 |
253 | a = {}
254 | # 去极值-标准化(前两列名为 factor_date,factor_symbol,最后一列为rate)
255 | for index, row in list(df.iteritems())[2:-1]:
256 | df[index] = standardize(filter_extreme_3sigma(row))
257 |
258 | a[index] = [t_value(df[index], df['rate'])]
259 |
260 |
261 | t_data = t_data.append(pd.DataFrame(a), ignore_index=True)
262 |
263 | for index, row in list(t_data.iteritems()):
264 | log.info(index)
265 | a = list(map(abs, list(t_data[index])))
266 | log.info(np.mean(np.array(a))
267 |
268 |
269 |
270 | # 获取所有股票每个交易日收盘价
271 | prices = get_price(context.iwencai_securities, last_year_date, date, '1d', ['close'], skip_paused = False, fq = 'pre',is_panel = 0)
272 |
273 | for stock in context.iwencai_securities:
274 | log.info(stock)
275 | # 股票收盘价列表(元素个数=交易日天数)
276 | stock_close = prices[stock]['close'].values
277 |
278 | # 股票收益率列表(元素个数=交易日天数-1)
279 | stock_rate = np.diff(stock_close) / stock_close[:-1]
280 |
281 | df_stock = df[df['factor_symbol'].isin([stock])].copy()
282 | log.info(df_stock)
283 |
284 | df_stock = df_stock.iloc[1:, :]
285 |
286 | df_stock['rate'] = stock_rate
287 | # log.info(df_stock)
288 |
289 | # 去极值-标准化
290 | for index, row in list(df_stock.iteritems())[1:-1]:
291 | df_stock[index] = standardize(filter_extreme_3sigma(row))
292 | df_stock = df_stock.fillna(0)
293 |
294 | log.info(df_stock)
295 | # 第三步,(按有效因子值将个股分为十档,计算每一档的IC、IR、根据IC特征设定方向,根据每组IC均值设定因子权重)
296 | # 随机选取50只股票来计算IC、IR、根据IC特征设定方向,根据每组IC均值设定因子权重
297 |
298 | q = query(
299 | factor.symbol,
300 | effneed
301 | ).filter(
302 | factor.symbol.in_(context.iwencai_securities),
303 | factor.date == trade_days[0]
304 | )
305 |
306 | df = get_factors(q)
307 |
308 | # 十档
309 | step = 10
310 |
311 | # 周期
312 | nd = 22
313 |
314 | # 用来存放各指标值
315 | need_data = {}
316 |
317 | # 循环每一个因子
318 | for col in df.columns.values[1:]:
319 | # 因子值从大到小排列
320 | a_hign = df.sort_values(col, axis=0, ascending=False)
321 | hign = a_hign.iloc[:, 0].values.tolist()
322 |
323 | # 用来存放十组数据IC均值、ic、pr值列表
324 | avr = [[], [], []]
325 |
326 | # 对所分五组进行迭代
327 | for sct in group(list(hign), step):
328 | # 用来存放一组内每期ic值
329 | ics = []
330 |
331 | # 每个分期时间点
332 | days = trade_days[0:len(trade_days):nd]
333 |
334 | for i, rate in enumerate(g_rate(sct, days, nd)):
335 | q = query(
336 | col
337 | ).filter(
338 | factor.symbol.in_(sct),
339 | factor.date == days[i]
340 | )
341 |
342 | df_sct = get_factors(q).fillna(0)
343 | df_sct[col] = standardize(filter_extreme_3sigma(df_sct[col])).fillna(0)
344 |
345 | # ic值
346 | ic = pd.Series(df_sct[col]).corr(pd.Series(list(rate)))
347 |
348 | ics.append(ic)
349 |
350 |
351 | # 一组内三个指标
352 | avr_ic, ir, pr = ics_return(ics)
353 | avr[0].append(avr_ic)
354 | avr[1].append(ir)
355 | avr[2].append(pr)
356 |
357 | need_data[col] = {
358 | 'avr_ic' : avr_6(avr[0]),
359 | 'avr_ir' : avr_6(avr[1]),
360 | 'avr_pr' : avr_6(avr[2])
361 | }
362 | # 对因子进行排序打分
363 | # 存放最终因子
364 | f_needs = {}
365 |
366 | data = pd.DataFrame(need_data).T.dropna()
367 |
368 | # 按照avr_ir降序
369 | data = data.sort_values('avr_ic', axis=0, ascending=False)
370 | data['avr_ic_score'] = list(reversed(range(len(data))))
371 |
372 | # 按照avr_ir排序分
373 | data = data.sort_values('avr_ir', axis=0, ascending=False)
374 | data['avr_ir_score'] = list(range(len(data)))
375 |
376 | # 分数相加
377 | data['total_socre'] = data['avr_ic_score'] + data['avr_ir_score']
378 | data = data.sort_values('total_socre', axis=0, ascending=False)
379 |
380 | # 获取排序后的因子
381 | fneeds = data._stat_axis.values.tolist()
382 | if len(fneeds) >= context.need_max:
383 | fneeds = fneeds[0:context.need_max]
384 |
385 | # 获取因子权重
386 | for i in fneeds:
387 | weight = data.loc[i, 'avr_ic']
388 |
389 | if data.loc[i, 'avr_pr'] > 0.5:
390 | weight = - weight
391 |
392 | f_needs[i] = {
393 | 'weight' : weight
394 | }
395 |
396 | return f_needs
397 |
398 | # 获得股票函数
399 | def get_stocks(context, bar_dict):
400 | pass
401 |
402 | # 每日运行函数
403 | def handle_bar(context, bar_dict):
404 | last_date = get_last_datetime().strftime('%Y%m%d')
405 | # log.info('上个交易日日期为:' + last_date)
406 |
407 | # 1. 获取上月月末日期
408 | def func_get_end_date_of_last_month(current_date):
409 | # 获取从上一个交易日前一个月中的所有交易日,日期排序从前至后
410 | trade_days = list(get_trade_days(None, current_date, count=30))
411 |
412 | # 转化为%Y%m%d格式
413 | for i in range(len(trade_days)):
414 | trade_days[i] = trade_days[i].strftime('%Y%m%d')
415 |
416 | # 只要交易日的date和当前交易日的月份不同即为上一个月月末日期,例如[20171013]-[20170928]
417 | # reversed反转序列,便于快速找到月末日期
418 | for date in reversed(trade_days):
419 | if date[5] != current_date[5]:
420 | return date
421 |
422 | log.info('找不到上个月末调仓日!')
423 | return
424 |
425 | # 获取收益率序列
426 | def g_rate(sct, days, nd):
427 | for i, day in enumerate(days):
428 | if i >= 1:
429 | prices = get_price(sct, days[i-1], days[i], '%dd' % nd, ['close'], skip_paused = False, fq = 'pre',is_panel = 1)['close']
430 | close_rate = (prices.iloc[-1] - prices.iloc[0]) / prices.iloc[0]
431 | yield close_rate
432 |
433 | # 计算一组数据的IC绝对值均值、正向比例、均值/标准差
434 | def ics_return(l):
435 |
436 | # 信息系数绝对值均值
437 | a = [abs(i) for i in l]
438 | avr_ic = sum(a) / len(l)
439 |
440 | # 信息比例
441 | b = np.array(l)
442 | if b.std() != 0:
443 | ir = b.mean() / b.std()
444 | else:
445 | ir = 0
446 |
447 | # 正向比例
448 | c = [i for i in l if i > 0]
449 | pr = len(c) / len(l)
450 | return avr_ic, ir, pr
451 |
452 | # 线性回归方程系数t检验值
453 | def t_value(x, y):
454 | x2 = sum((x - np.mean(x)) ** 2)
455 | xy = sum((x - np.mean(y)) * (y - np.mean(y)))
456 |
457 | #回归参数的最小二乘估计
458 | beta1 = xy / x2
459 | beta0 = np.mean(y) - beta1 * np.mean(x)
460 | #输出线性回归方程
461 | # print('y=',beta0,'+',beta1,'*x')
462 |
463 | #方差
464 | sigma2 = sum((y - beta0 - beta1 * x) ** 2) / len(x)
465 | #标准差
466 | sigma = np.sqrt(sigma2)
467 | #求t值
468 | t = beta1 * np.sqrt(x2) / sigma
469 | return t
470 |
471 | # 求一组数的均值并保留6位小数
472 | def avr_6(l):
473 | b = round(np.array(l).mean(), 6)
474 | return b
475 |
476 | # 3 sigma 去极值
477 | def filter_extreme_3sigma(series, n=3):
478 | # 均值
479 | mean = series.mean()
480 |
481 | # 标准差
482 | std = series.std()
483 |
484 | max_range = mean + n*std
485 | min_range = mean - n*std
486 |
487 | # clip函数用于将超出范围的值填充为min_range,max_range
488 | return np.clip(series, min_range, max_range)
489 |
490 | # z-score标准化
491 | def standardize(series):
492 | std = series.std()
493 | mean = series.mean()
494 |
495 | return (series - mean) / std
496 |
497 | # 分组迭代器
498 | def group(l, s):
499 | length = len(l)
500 | for i in range(s):
501 | que = []
502 | left = i * (length // s)
503 | if (i+1) * (length // s) < length:
504 | right = (i+1) * (length // s)
505 | else:
506 | right = length
507 | for j in l[left:right]:
508 | que.append(j)
509 | yield que
510 |
511 | # 每日运行函数
512 | def handle_bar(context, bar_dict):
513 | last_date = get_last_datetime().strftime('%Y%m%d')
514 | if last_date != context.last_date and len(list(context.portfolio.stock_account.positions.keys())) > 0:
515 | # 如果不是调仓日且有持仓,择时买入
516 | trade_timing(context, bar_dict)
517 |
518 | # 择时买股函数
519 | def trade_timing(context, bar_dict):
520 | pass
521 |
522 | import pandas as pd
523 | import numpy as np
524 | import datetime, time
525 | from dateutil.relativedelta import relativedelta
526 | import talib
527 | import talib as ta
528 | import math, random
529 | from scipy.stats import ttest_ind
530 | from scipy.stats import levene
531 | from sklearn.linear_model import LinearRegression
532 | import copy
533 | g.record = pd.DataFrame({'symbol': [], 'add_time': [], 'last_buy_price': []})
534 |
535 |
536 | def init(context):
537 | g.num = 0
538 | log.info('begin')
539 | # get_iwencai('沪深300')
540 | # set_benchmark('000905.SH')
541 | # 设置最大持股数
542 | context.stoplossmultipler = 0.95
543 | # 设置交易股票数量
544 | context.max_stocks = 40
545 | matrix1 = [[0 for i in range(10)]]
546 | g.matrix = matrix1
547 | # 用于计数
548 | g.means1 = g.means2 = g.means3 = g.means4 = g.means5 = g.means6 = g.means7 = 0
549 |
550 | # 最大持股数量(大小盘各一半)
551 | context.hold_max = 40
552 |
553 | # 初始因子池(包括技术因子和财务因子在内的共计107个初始因子)
554 | context.need = ['factor.pe', # 市盈率
555 | 'factor.pb', # 市净率
556 | 'factor.pcf_cash_flow_ttm', # 市现率PCF
557 | 'factor.ps', # 市销率
558 | 'factor.dividend_rate', # 股息率
559 | 'factor.market_cap', # 总市值
560 | 'factor.current_market_cap', # 流通市值
561 | 'factor.capitalization', # 总股本
562 | 'factor.circulating_cap', # 流通股本
563 | 'factor.current_ratio', # 流动比率
564 | 'factor.equity_ratio', # 产权比率负债合计/归属母公司股东的权益
565 | 'factor.quick_ratio', # 速动比率
566 | 'factor.tangible_assets_liabilities', # 有形资产/负债合计
567 | 'factor.tangible_assets_int_liabilities', # 有形资产/带息债务
568 | 'factor.net_debt_equity', # 净债务/股权价值
569 | 'factor.long_term_debt_to_opt_capital_ratio', # 长期债务与营运资金比率
570 | 'factor.tangible_assets_net_liabilities', # 有形资产/净债务
571 | 'factor.overall_income_growth_ratio', # 营业总收入同比增长率
572 | # 'factor.net_cashflow_psg_rowth_ratio',# 每股经营活动产生的现金流量净额同比增长率(报错,原因询问客服ing)
573 | # 'factor.opt_profit_grow_ratio',# 营业利润同比增长率
574 | # 'factor.total_profit_growth_ratio',# 利润总额同比增长率
575 | 'factor.diluted_net_asset_growth_ratio', # 净资产收益率摊薄同比增长率
576 | 'factor.net_cashflow_from_opt_act_growth_ratio', # 经营活动产生的现金流量净额同比增长率
577 | 'factor.net_profit_growth_ratio', # 净利润同比增长率
578 | # 'factor.basic_pey_ear_growth_ratio',# 基本每股收益同比增长率
579 | 'factor.turnover_of_overall_assets', # 总资产周转率
580 | 'factor.turnover_ratio_of_account_payable', # 应付账款周转率
581 | 'factor.turnover_of_current_assets', # 流动资产周转率
582 | 'factor.turnover_of_fixed_assets', # 固定资产周转率
583 | 'factor.cash_cycle', # 现金循环周期
584 | 'factor.inventory_turnover_ratio', # 存货周转率
585 | 'factor.turnover_ratio_of_receivable', # 应收账款周转率
586 | 'factor.weighted_roe', # 净资产收益率roe加权
587 | 'factor.overall_assets_net_income_ratio', # 总资产净利率roa
588 | 'factor.net_profit_margin_on_sales', # 销售净利率
589 | 'factor.before_tax_profit_div_income', # 息税前利润/营业总收入
590 | 'factor.sale_cost_div_income', # 销售费用/营业总收入
591 | 'factor.roa', # 总资产报酬率roa
592 | 'factor.ratio_of_sales_to_cost', # 销售成本率
593 | 'factor.net_profit_div_income', # 净利润/营业总收入
594 | 'factor.opt_profit_div_income', # 营业利润/营业总收入
595 | 'factor.opt_cost_div_income', # 营业总成本/营业总收入
596 | 'factor.administration_cost_div_income', # 管理费用/营业总收入
597 | 'factor.financing_cost_div_income', # 财务费用/营业总收入
598 | 'factor.impairment_loss_div_income', # 资产减值损失/营业总收
599 | # 以下为技术因子
600 | 'factor.vr_rate', # 成交量比率
601 | 'factor.vstd', # 成交量标准差
602 | 'factor.arbr', # 人气意愿指标
603 | 'factor.srdm', # 动向速度比率
604 | 'factor.vroc', # 量变动速率
605 | 'factor.vrsi', # 量相对强弱指标
606 | 'factor.cr', # 能量指标
607 | 'factor.mfi', # 资金流向指标
608 | 'factor.vr', # 量比
609 | 'factor.mass', # 梅丝线
610 | 'factor.obv', # 能量潮
611 | 'factor.pvt', # 量价趋势指标
612 | 'factor.wad', # 威廉聚散指标
613 | 'factor.bbi', # 多空指数
614 | 'factor.mtm', # 动力指标
615 | 'factor.dma', # 平均线差
616 | 'factor.ma', # 简单移动平均
617 | 'factor.macd', # 指数平滑异同平均
618 | 'factor.expma', # 指数平均数
619 | 'factor.priceosc', # 价格振荡指标
620 | 'factor.trix', # 三重指数平滑平均
621 | 'factor.dbcd', # 异同离差乖离率
622 | 'factor.dpo', # 区间震荡线
623 | 'factor.psy', # 心理指标
624 | 'factor.vma', # 量简单移动平均
625 | 'factor.vmacd', # 量指数平滑异同平均
626 | 'factor.vosc', # 成交量震荡
627 | 'factor.tapi', # 加权指数成交值
628 | 'factor.micd', # 异同离差动力指数
629 | 'factor.rccd', # 异同离差变化率指数
630 | 'factor.ddi', # 方向标准差偏离指数
631 | 'factor.bias', # 乖离率
632 | 'factor.cci', # 顺势指标
633 | 'factor.kdj', # 随机指标
634 | 'factor.lwr', # L威廉指标
635 | 'factor.roc', # 变动速率
636 | 'factor.rsi', # 相对强弱指标
637 | 'factor.si', # 摆动指标
638 | 'factor.wr', # 威廉指标
639 | 'factor.wvad', # 威廉变异离散量
640 | 'factor.bbiboll', # BBI多空布林线
641 | 'factor.cdp', # 逆势操作
642 | 'factor.env', # ENV指标
643 | 'factor.mike', # 麦克指标
644 | 'factor.adtm', # 动态买卖气指标
645 | 'factor.mi', # 动量指标
646 | 'factor.rc', # 变化率指数
647 | 'factor.srmi', # SRMIMI修正指标
648 | 'factor.dptb', # 大盘同步指标
649 | 'factor.jdqs', # 阶段强势指标
650 | 'factor.jdrs', # 阶段弱势指标
651 | 'factor.zdzb', # 筑底指标
652 | 'factor.atr', # 真实波幅
653 | 'factor.std', # 标准差
654 | 'factor.vhf', # 纵横指标
655 | 'factor.cvlt'] # 佳庆离散指标
656 |
657 | # 最大因子数目
658 | context.need_max = 10
659 |
660 | # 上一次调仓期
661 | context.last_date = ''
662 | # 设置调仓周期,每月倒数第一个交易日运行
663 | run_monthly(func=reallocate, date_rule=-1)
664 |
665 | # 设置因子测试周期为12个月
666 | context.need_tmonth = 12
667 |
668 | # 选择沪深300
669 | get_iwencai('沪深300', 'hs_300')
670 |
671 | # 选择中证500
672 | get_iwencai('中证500', 'zz_500')
673 |
674 |
675 | def reallocate(context, bar_dict):
676 | # 获取上一个交易日的日期
677 | date = get_last_datetime().strftime('%Y%m%d')
678 | log.info('上个交易日日期为:' + date)
679 |
680 | # 获取上个月末调仓日期
681 | context.last_date = func_get_end_date_of_last_month(date)
682 |
683 | log.info('上月月末调仓日期为:' + context.last_date)
684 |
685 | hs_needs, zz_needs = get_needs(context, bar_dict, date, context.hs_300), get_needs(context, bar_dict, date,
686 | context.zz_500)
687 | # log.info('这是沪深300股票池的因子:')
688 | # log.info(hs_needs)
689 |
690 | # log.info('这是中证500股票池的因子:')
691 | # log.info(zz_needs)
692 |
693 | # 用来存放大小盘股票,0是小盘,1是大盘
694 | tempstocks = [[], []]
695 | dapan_stocks = get_stocks(context, bar_dict, zz_needs, date, context.zz_500)
696 | for stock in dapan_stocks:
697 | tempstocks[0].append(stock)
698 | tempstocks[1].append(1)
699 | xiaopan_stocks = get_stocks(context, bar_dict, hs_needs, date, context.zz_500)
700 | for stock in xiaopan_stocks:
701 | tempstocks[0].append(stock)
702 | tempstocks[1].append(0)
703 | log.info(tempstocks)
704 | # 第一个月
705 | if g.matrix[0][0] == 0:
706 | log.info("第一个月")
707 | log.info(g.matrix)
708 | g.matrix.pop(0) # 将此股票从数组中删除
709 | log.info(g.matrix)
710 | for num,tempstock in enumerate(tempstocks[0]):
711 | matrix2 = [0 for i in range(10)]
712 | matrix2[0] = tempstock
713 | # 1是大盘 0是小盘
714 | matrix2[8] = tempstocks[1][num]
715 | matrix3 = copy.deepcopy(matrix2)
716 | g.matrix.append(matrix3)
717 | log.info(g.matrix)
718 | else:
719 | # 先清除标记
720 | for n, s in enumerate(g.matrix):
721 | g.matrix[n][9] = 0
722 | # 需要比较是否与前一个月重复
723 | for num, tempstock in enumerate(tempstocks[0]):
724 | flag_repeat = 0
725 | for num, stock in enumerate(g.matrix):
726 | if stock[0] == tempstock:
727 | flag_repeat = 1
728 | # 用来标记是否新买入的股票
729 | g.matrix[num][9] = 1
730 | break
731 | if not flag_repeat:
732 | # 不重复 就新加
733 | matrix2 = [0 for i in range(10)]
734 | matrix2[0] = tempstock
735 | # 用来标记是否新买入的股票
736 | matrix2[9] = 1
737 | matrix3 = copy.deepcopy(matrix2)
738 | g.matrix.append(matrix3)
739 | # 将这个月不合适的股票进行清仓
740 | for num, stock in enumerate(g.matrix):
741 | if stock[9] == 0:
742 | sec = g.matrix[num][0]
743 | order_target_value(sec, 0) # 清仓离场
744 | g.record = g.record[g.record['symbol'] != sec] # 将卖出股票的记录清空
745 | g.matrix.pop(num) # 将此股票从数组中删除
746 |
747 | # 用于计数
748 | g.means1 = g.means2 = g.means3 = g.means4 = g.means5 = g.means6 = 0
749 |
750 |
751 | # log.info(tempstocks)
752 |
753 | # 获得因子函数
754 | def get_needs(context, bar_dict, date, stocks):
755 | time_tuple = time.strptime(date, '%Y%m%d')
756 | year, month, day = time_tuple[:3]
757 | # 转化为datetime.date类型
758 | date = datetime.date(year, month, day)
759 |
760 | last_year_date =(date - relativedelta(years = 1)).strftime('%Y%m%d')
761 | # log.info('上个交易日前一年的日期为:' + last_year_date)
762 |
763 | # 上一年内的所有交易日期
764 | trade_days = get_trade_days(last_year_date, date).strftime('%Y-%m-%d')
765 |
766 | # 所有因子
767 | need_all = ','.join(context.need)
768 |
769 | # 1.分组单因子有效性检验,参考资料:https://www.ricequant.com/community/topic/702/%E5%8D%95%E4%B8%80%E5%9B%A0%E5%AD%90%E6%9C%89%E6%95%88%E6%80%A7%E6%A3%80%E6%B5%8B/2
770 | # 每隔25天(间隔越短,收益就会越随机)
771 | period = 25
772 |
773 | # 有效因子列表
774 | eff_need = []
775 |
776 | # 每组股票数10%只股票
777 | num = math.ceil(0.1 * len(stocks))
778 |
779 | # 获取开始日期因子值
780 | q = query(
781 | factor.symbol,
782 | need_all
783 | ).filter(
784 | factor.symbol.in_(stocks),
785 | # 20180430的数据消失,原因是:last_year_date可能不是交易日,这样就获取不到数据,因此为trade_days[0]
786 | factor.date == trade_days[0]
787 | )
788 |
789 | df = get_factors(q)
790 |
791 | for col in df.columns.values[1:]:
792 | # 分别按正序和倒序提取因子值前10%和后10%的股票
793 | a_hign = df.sort_values(col, axis=0, ascending=False)
794 | a_low = df.sort_values(col, axis=0, ascending=True)
795 |
796 | hign = a_hign.iloc[0:num, 0].values.tolist()
797 | low = a_low.iloc[0:num, 0].values.tolist()
798 | hign_price = get_price(hign, last_year_date, date, '1d', ['close'], skip_paused = False, fq = 'pre', is_panel = 1)
799 | low_price = get_price(low, last_year_date, date, '1d', ['close'], skip_paused = False, fq = 'pre', is_panel = 1)
800 |
801 | hign_price = hign_price['close']
802 | low_price = low_price['close']
803 |
804 | # 计算平均每组股票每日价格
805 | high_series = hign_price.T.mean()
806 | low_series = low_price.T.mean()
807 |
808 | # 计算收益率
809 | hign_rr = high_series.pct_change(period).dropna()
810 | low_rr = low_series.pct_change(period).dropna()
811 |
812 | # 判断两组数据的方差是否显著不同
813 | le = levene(hign_rr, low_rr)
814 |
815 | # 判断两组数据的均值是否显著不同
816 | tt = ttest_ind(hign_rr, low_rr, equal_var = False)
817 |
818 | # 取置信度为0.05
819 | if le.pvalue < 0.05 and tt.pvalue < 0.05:
820 | eff_need.append('factor.%s' % col)
821 |
822 | # 第一步有效因子个数
823 | log.info(len(eff_need))
824 |
825 | effneed = ','.join(eff_need)
826 |
827 | # 第二步,随机选取num只股票来计算IC、IR、根据IC特征设定方向,根据每组IC均值设定因子权重(降低运算数量)
828 |
829 | # 随机取两组num数量的股票
830 | # gp = 2
831 | q = query(
832 | factor.symbol,
833 | effneed
834 | ).filter(
835 | factor.symbol.in_(stocks),
836 | factor.date == trade_days[0]
837 | )
838 |
839 | df = get_factors(q)
840 |
841 | # step档
842 | step = 10
843 |
844 | # 周期
845 | nd = 22
846 |
847 | # 用来存放各指标值
848 | need_data = {}
849 |
850 | # 循环每一个因子
851 | for col in df.columns.values[1:]:
852 | # 因子值从大到小排列
853 | a_hign = df.sort_values(col, axis=0, ascending=False)
854 | hign = a_hign.iloc[:, 0].values.tolist()
855 |
856 | # 用来存放step组数据IC均值、ic、pr值列表
857 | avr = [[], [], []]
858 |
859 | # group()将一个list分为step组
860 | for sct in group(list(hign), step):
861 |
862 | # 用来存放一组内每期ic值
863 | ics = []
864 |
865 | # 每个分期时间点
866 | days = trade_days[0:len(trade_days):nd]
867 |
868 | for i, rate in enumerate(g_rate(sct, days, nd)):
869 | q = query(
870 | col
871 | ).filter(
872 | factor.symbol.in_(sct),
873 | factor.date == days[i]
874 | )
875 |
876 | df_sct = get_factors(q).fillna(0)
877 |
878 | # col = df_sct.columns.values[0]
879 | df_sct[col] = standardize(filter_extreme_3sigma(df_sct[col])).fillna(0)
880 |
881 | # ic值
882 | ic = pd.Series(df_sct[col]).corr(pd.Series(list(rate)))
883 |
884 | ics.append(ic)
885 |
886 |
887 | # 一组内三个指标
888 | avr_ic, ir, pr = ics_return(ics)
889 | avr[0].append(avr_ic)
890 | avr[1].append(ir)
891 | avr[2].append(pr)
892 |
893 | need_data[col] = {
894 | 'avr_ic' : avr_6(avr[0]),
895 | 'avr_ir' : avr_6(avr[1]),
896 | 'avr_pr' : avr_6(avr[2])
897 | }
898 |
899 | # 对因子进行排序打分
900 | # 存放最终因子
901 | f_needs = {}
902 |
903 | data = pd.DataFrame(need_data).T.dropna()
904 |
905 | # 按照avr_ir降序打分
906 | data = data.sort_values('avr_ic', axis=0, ascending=False)
907 | data['avr_ic_score'] = list(reversed(range(len(data))))
908 |
909 | # 按照avr_ir降打分
910 | data = data.sort_values('avr_ir', axis=0, ascending=False)
911 | data['avr_ir_score'] = list(range(len(data)))
912 |
913 | # 分数相加
914 | data['total_socre'] = data['avr_ic_score'] + data['avr_ir_score']
915 | data = data.sort_values('total_socre', axis=0, ascending=False)
916 |
917 | # 获取排序后的因子
918 | fneeds = data._stat_axis.values.tolist()
919 | if len(fneeds) >= context.need_max:
920 | fneeds = fneeds[0:context.need_max]
921 |
922 | # 获取因子权重
923 | for i in fneeds:
924 | weight = data.loc[i, 'avr_ic']
925 |
926 | if data.loc[i, 'avr_pr'] < 0.5:
927 | weight = - weight
928 |
929 | f_needs['factor.%s' % i] = {
930 | 'weight' : weight
931 | }
932 |
933 | return f_needs
934 |
935 |
936 | # 获得股票函数
937 | def get_stocks(context, bar_dict, needs, date, stocks):
938 | # needs为如下字典:
939 | # 例{'factor.dbcd': {'weight': -0.146216}, 'factor.weighted_roe': {'weight': 0.108531}, 'factor.turnover_of_current_assets': {'weight': -0.111679}, 'factor.turnover_of_overall_assets': {'weight': -0.148806}, 'factor.administration_cost_div_income': {'weight': 0.118929}}
940 | need_all = ','.join(list(needs.keys()))
941 | q = query(
942 | factor.symbol,
943 | need_all
944 | ).filter(
945 | factor.symbol.in_(stocks),
946 | factor.date == date
947 | )
948 | df = get_factors(q).fillna(0)
949 |
950 | # 用来存放股票分值
951 | score = []
952 |
953 | weight = [needs[i]['weight'] for i in needs]
954 |
955 | for i in range(len(df)):
956 | result = sum(list(map(lambda x, y: x * y, list(df.iloc[i, 1:]), weight)))
957 | score.append(result)
958 |
959 | df['score'] = score
960 |
961 | df = df.sort_values('score', axis=0, ascending=False)
962 |
963 | # 大小盘默认持股数
964 | hold = int(context.hold_max / 2)
965 |
966 | # 存放选取的股票
967 | stocks_c = list(df.iloc[0: hold, 0])
968 |
969 | return stocks_c
970 |
971 |
972 | # 每日运行函数
973 | def handle_bar(context, bar_dict):
974 | last_date = get_last_datetime().strftime('%Y%m%d')
975 | trade_before_new(context,bar_dict)
976 | trade_new(context,bar_dict)
977 |
978 |
979 | # log.info('上个交易日日期为:' + last_date)
980 | def trade_new(context,bar_dict):
981 | a = get_datetime()
982 | # current_universe = context.iwencai_securities # 获取当前除停牌外的所有可供投资股票(universe)
983 |
984 |
985 | # security_position = context.portfolio.stock_account.positions.keys() # 字典数据,上一K线结束后的有效证券头寸,即持仓数量大于0的证券及其头寸
986 | for index,jj in enumerate(g.matrix):
987 |
988 | stock = g.matrix[index][0] # 获得备选列表中的股票代码
989 | sec = stock
990 | curent = get_candle_stick([stock], end_date=a, fre_step='1m', fields=['open', 'close', 'high', 'low', 'volume'],
991 | skip_paused=False, fq=None, bar_count=5, is_panel=0)
992 | current_price = curent[stock]['open'][-1]
993 |
994 | value = history([stock], ['close', 'open', 'low', 'turnover', 'high'], 20, '1d', False, None)
995 |
996 | value1 = history(stock, ['close', 'open', 'low', 'volume', 'high'], 60, '1d', True, 'pre')
997 | value1 = value1.dropna()
998 | close = value1.close.values
999 | log.info(close,value1,stock)
1000 | low = value1.low.values
1001 | # high = value1['high'].as_matrix()
1002 | high = value1.high.values
1003 | vol = value1['volume'].as_matrix()
1004 | current_price = close[-1] # 获得当前时刻价格
1005 | # short_ma = ta.MA(close, 5) # 5天均线
1006 | # long_ma = ta.MA(close, 10) # 10天均线
1007 | # mid_ma = ta.MA(close, 20) # 20天均线
1008 | # up, mid,down = talib.BBANDS(close, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0)
1009 | # print(up)
1010 | # 数据处理(策略部分):计算真实波幅,
1011 | try:
1012 | atr = talib.ATR(high, low, close, 20)[-1] # 计算真实波幅
1013 | except:
1014 | print(high,low,close)
1015 |
1016 | upperband, middleband, lowerband = talib.BBANDS(np.asarray(value[stock]['close']), timeperiod=10, nbdevup=1.96,
1017 | nbdevdn=1.96, matype=0)
1018 | # log.info(g.matrix[index][0],g.matrix[index][1],g.matrix[index][3],g.matrix[index][4])
1019 | if g.matrix[index][8] == 1:
1020 | for t in g.record['symbol']:
1021 | if t == sec:
1022 | flag_buy = 1
1023 | if (g.record[g.record['symbol'] == sec]['last_buy_price'].empty):
1024 | continue
1025 | else:
1026 |
1027 | last_price = float(g.record[g.record['symbol'] == sec]['last_buy_price']) # 上一次的买入价格(查找、判断、删除)
1028 | add_price = last_price + 0.5 * atr # 计算是否加仓的判断价格
1029 | add_price_top = last_price + 1.5 * atr
1030 | add_unit = float(g.record[g.record['symbol'] == sec]['add_time']) # 已加仓次数
1031 | if current_price > add_price and add_unit < 4: # 价格上涨超过0.5N并且加仓次数小于4次
1032 | log.info("加仓")
1033 | log.info(sec)
1034 | unit = calcUnit(context.portfolio.portfolio_value, atr) # 计算加仓时应购入的股票数量
1035 | log.info(unit)
1036 | try:
1037 | if g.record[g.record['symbol'] == sec]['add_time'] == 0:
1038 | order(sec, 1.5 * unit) # 买入2unit的股票
1039 | log.info("买入2unit的股票")
1040 | elif g.record[g.record['symbol'] == sec]['add_time'] == 1:
1041 | order(sec, 1.25 * unit) # 买入1.5unit的股票
1042 | log.info("买入1.5unit的股票")
1043 | elif g.record[g.record['symbol'] == sec]['add_time'] == 2:
1044 | order(sec, 1 * unit) # 买入1unit的股票
1045 | log.info("买入1unit的股票")
1046 | elif g.record[g.record['symbol'] == sec]['add_time'] == 3:
1047 | order(sec, 0.7 * unit) # 买入1unit的股票
1048 | log.info("买入0.5unit的股票")
1049 | except:
1050 | order(sec, 1.5 * unit) # 买入2unit的股票
1051 | log.info("买入2unit的股票")
1052 | g.record.loc[g.record['symbol'] == sec, 'add_time'] = g.record[g.record['symbol'] == sec][
1053 | 'add_time'] + 1 # 加仓次数+1(先找到值再找到位置,然后赋值)
1054 | g.record.loc[g.record['symbol'] == sec, 'last_buy_price'] = current_price # 加仓次数+1
1055 | # 策略部分(离场:止损或止盈):当前价格下穿到BOLL上轨或相对上个买入价下跌 2ATR时(止损)或当股价5日均线下穿10日均线并且量均线死叉,清仓离场
1056 | if current_price < (last_price - 2 * atr) or current_price < low[-10:-1].min():
1057 | log.info("抛售")
1058 | log.info(sec)
1059 | g.means7 += 1
1060 | order_target_value(sec, 0) # 清仓离场
1061 |
1062 | g.record = g.record[g.record['symbol'] != sec] # 将卖出股票的记录清空
1063 |
1064 | if (g.matrix[index][1] == True and g.matrix[index][3] == True and g.matrix[index][4] == True) or (
1065 | g.matrix[index][6] == True and flag_buy == 0):
1066 | g.num += 1
1067 | log.info("购入大盘")
1068 | order_target_value(stock, context.portfolio.available_cash / 40)
1069 | if len(g.record) != 0:
1070 | g.record = g.record[
1071 | g.record['symbol'] != stock] # 清空g.record中sec过期的记录,因为如果之前记录就有000001,现在将这个股票建仓,就要先删除原来为symbol的记录。
1072 |
1073 | g.record = g.record.append(pd.DataFrame(
1074 | {'symbol': [stock], 'add_time': [1], 'last_buy_price': [current_price]})) # 记录股票,加仓次数及买入价格
1075 | continue
1076 |
1077 | if g.matrix[index][8] == 0:
1078 | flag_buy = 0
1079 | for t in g.record['symbol']:
1080 | if t == sec:
1081 | flag_buy = 1
1082 | if (g.record[g.record['symbol'] == sec]['last_buy_price'].empty):
1083 | continue
1084 | else:
1085 |
1086 | last_price = float(g.record[g.record['symbol'] == sec]['last_buy_price']) # 上一次的买入价格(查找、判断、删除)
1087 | add_price = last_price + 0.5 * atr # 计算是否加仓的判断价格
1088 | add_price_top = last_price + 1.5 * atr
1089 | add_unit = float(g.record[g.record['symbol'] == sec]['add_time']) # 已加仓次数
1090 | if current_price > add_price and add_unit < 4 and current_price >= (
1091 | last_price - 2 * atr): # 价格上涨超过0.5N并且加仓次数小于4次
1092 | log.info("加仓")
1093 | log.info(sec)
1094 | unit = calcUnit(context.portfolio.portfolio_value, atr) # 计算加仓时应购入的股票数量
1095 | log.info(unit)
1096 | try:
1097 | if g.record[g.record['symbol'] == sec]['add_time'] == 0:
1098 | order(sec, 1.5 * unit) # 买入1unit的股票
1099 | elif g.record[g.record['symbol'] == sec]['add_time'] == 1:
1100 | order(sec, 1.25 * unit) # 买入1unit的股票
1101 | elif g.record[g.record['symbol'] == sec]['add_time'] == 2:
1102 | order(sec, 1 * unit) # 买入1unit的股票
1103 | elif g.record[g.record['symbol'] == sec]['add_time'] == 3:
1104 | order(sec, 0.7 * unit) # 买入1unit的股票
1105 | except:
1106 | order(sec, 1.5 * unit) # 买入1unit的股票
1107 | g.record.loc[g.record['symbol'] == sec, 'add_time'] = g.record[g.record['symbol'] == sec][
1108 | 'add_time'] + 1 # 加仓次数+1(先找到值再找到位置,然后赋值)
1109 | g.record.loc[g.record['symbol'] == sec, 'last_buy_price'] = current_price # 加仓次数+1
1110 | # 策略部分(离场:止损或止盈):当前价格下穿到BOLL上轨或相对上个买入价下跌 2ATR时(止损)或当股价5日均线下穿10日均线并且量均线死叉,清仓离场
1111 | if current_price < (last_price - 2 * atr) or current_price < low[-10:-1].min():
1112 | log.info("抛售")
1113 | g.means7 += 1
1114 | log.info(sec)
1115 | order_target_value(sec, 0) # 清仓离场
1116 |
1117 | g.record = g.record[g.record['symbol'] != sec] # 将卖出股票的记录清空
1118 |
1119 | if ((g.matrix[index][1] == True and g.matrix[index][3] == True and g.matrix[index][4] == True) or (
1120 | g.matrix[index][6] == False) and (flag_buy == 0)):
1121 |
1122 | log.info("购入小盘")
1123 | g.num += 1
1124 | log.info(stock)
1125 | # log.info('g.matrix[index][4]==True and g.matrix[index][6]==True and g.matrix[index][2]==True')
1126 | order_target_value(stock, context.portfolio.available_cash / 40)
1127 | if len(g.record) != 0:
1128 | g.record = g.record[
1129 | g.record['symbol'] != stock] # 清空g.record中sec过期的记录,因为如果之前记录就有000001,现在将这个股票建仓,就要先删除原来为symbol的记录。
1130 |
1131 | g.record = g.record.append(pd.DataFrame(
1132 | {'symbol': [stock], 'add_time': [1], 'last_buy_price': [current_price]})) # 记录股票,加仓次数及买入价格
1133 | continue
1134 |
1135 |
1136 | def trade_before_new(context,bar_dict):
1137 | if g.matrix[0][0] == 0:
1138 | reallocate(context, bar_dict)
1139 | log.info(g.matrix)
1140 | a = get_datetime()
1141 | xiaopan = '000905.SH'
1142 | dapan = '000300.SH'
1143 | Indexprice_xiao = get_price(xiaopan, None, time.strftime("%Y%m%d"), '1d', ['close'], True, None, 100, is_panel=1)
1144 | Indexprice_da = get_price(dapan, None, time.strftime("%Y%m%d"), '1d', ['close'], True, None, 100, is_panel=1)
1145 | strong_weak = np.log(Indexprice_xiao.close) - np.log(Indexprice_da.close)
1146 | strong_weak = strong_weak[20:]
1147 | lowerband_new = pd.rolling_mean(strong_weak, 20) - 2 * pd.rolling_std(strong_weak, 20)
1148 | up_new = pd.rolling_mean(strong_weak, 20) + 2 * pd.rolling_std(strong_weak, 20)
1149 | for index,jj in enumerate(g.matrix):
1150 |
1151 | # 基本数据获取
1152 | stock = g.matrix[index][0] # 获得备选列表中的股票代码
1153 | log.info(stock)
1154 | sec = stock
1155 | curent = get_candle_stick([stock], end_date=a, fre_step='1m', fields=['open', 'close', 'high', 'low', 'volume'],
1156 | skip_paused=False, fq=None, bar_count=5, is_panel=0)
1157 | value = history([stock], ['close', 'open', 'low', 'turnover', 'high'], 20, '1d', False, None)
1158 |
1159 | value1 = history(stock, ['close', 'open', 'low', 'volume', 'high'], 110, '1d', True, 'pre')
1160 | value1 = value1.dropna()
1161 | close = value1.close.values
1162 | low = value1.low.values
1163 | high = value1['high'].as_matrix()
1164 | vol = value1['volume'].as_matrix()
1165 |
1166 | current_price = close[-1] # 获得当前时刻价格
1167 | log.info(close)
1168 | short_ma = ta.MA(close, 5) # 5天均线
1169 | long_ma = ta.MA(close, 10) # 10天均线
1170 | mid_ma = ta.MA(close, 20) # 20天均线
1171 | up, mid, low = talib.BBANDS(close, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0)
1172 | # 数据处理(策略部分):计算真实波幅,
1173 | # atr = talib.ATR(high, low, close, 20)[-1] # 计算真实波幅
1174 |
1175 | # 第一部分 计算#VOL5上穿VOL10
1176 |
1177 | short_vol = ta.MA(vol, 5) # 过去5天均量线
1178 | long_vol = ta.MA(vol, 10) # 过去10天均量线
1179 |
1180 | if short_vol[-1] > short_vol[-2] and long_vol[-1] > long_vol[-2] and short_vol[-2] <= long_vol[-2] and \
1181 | short_vol[-1] > long_vol[-1]:
1182 | g.matrix[index][1] = True # VOL5上穿VOL10且多头向上排列,可建仓标志
1183 | g.means1 += 1
1184 | else:
1185 | g.matrix[index][1] = False # 不可建仓标志
1186 |
1187 | if (short_vol[-2] >= long_vol[-2] and short_vol[-1] < long_vol[-1]):
1188 | g.matrix[index][2] = True # VOL5下穿VOL10,可清仓标志
1189 | g.means2 += 1
1190 | else:
1191 | g.matrix[index][2] = False # 不可清仓标志
1192 |
1193 | # 第二部分 #MACD中的DIF>DEA & DIF>0 & DEA>0
1194 |
1195 | close_macd = value[stock]['close'].as_matrix()
1196 |
1197 | DIF, DEA, MACD = talib.MACD(close, fastperiod=12, slowperiod=26,
1198 | signalperiod=9) # talib提供的MACD计算函数,计算DIF,DEF以及MACD的取值
1199 |
1200 | if DIF[-1] > DEA[-1] and DIF[-1] > 0 and DEA[-1] > 0:
1201 | g.matrix[index][3] = True
1202 | g.means3 += 1
1203 |
1204 | else:
1205 | g.matrix[index][3] = False
1206 |
1207 | # 第三部分 是否触发MA5与MA10建仓或离场标志
1208 |
1209 | if short_ma[-1] > mid_ma[-2] and short_ma[-1] > short_ma[-2] and mid_ma[-1] > mid_ma[-2] and short_ma[-1] > \
1210 | mid_ma[-1] and mid_ma[-1] > long_ma[-1] and short_ma[-2] <= mid_ma[-2] and short_ma[-1] > mid_ma[-1]:
1211 | g.matrix[index][4] = True # MA5上穿MA10且三线多头向上排列,可建仓标志
1212 | g.means4 += 1
1213 | else:
1214 | g.matrix[index][4] = False # 不可建仓标志
1215 | if (short_ma[-2] >= mid_ma[-2] and short_ma[-1] < mid_ma[-1]):
1216 | g.matrix[index][5] = True # MA5下穿MA10或者当前价跌破MA10,可清仓标志
1217 | g.means5 += 1
1218 | else:
1219 | g.matrix[index][5] = False # 不可清仓标志
1220 |
1221 | # 第四部分 创新穿越次数
1222 |
1223 | # value_new = history([stock], ['close', 'open', 'low', 'turnover', 'high'], 110, '1d', False, None)
1224 | # upperband_new, middleband_new, lowerband_new = talib.BBANDS(np.asarray(value_new[stock]['close']),
1225 | # timeperiod=10, nbdevup=1.96, nbdevdn=1.96, matype=0)
1226 | flag_shangchuan = 0
1227 | flag_xiachuan = 0
1228 | flag_shangchuancishu = 0
1229 | flag_xiachuancishu = 0
1230 |
1231 | # for t in range(9, 109):
1232 | # if flag_shangchuan == 0:
1233 | # if strong_weak[t] < upperband_new[t]:
1234 | # # 定义标识数
1235 | # flag_shangchuan = 1
1236 | # if flag_shangchuan == 1:
1237 | # if strong_weak[t] > upperband_new[t]:
1238 | # flag_shangchuancishu = flag_shangchuancishu + 1
1239 | # flag_shangchuan = 2
1240 | # if flag_shangchuan == 2:
1241 | # if strong_weak[t] > upperband_new[t]:
1242 | # flag_shangchuan = 2
1243 | # if strong_weak[t] < upperband_new[t]:
1244 | # flag_shangchuan = 0
1245 | if (strong_weak[-2] >= lowerband_new[-2] and strong_weak[-1] <= lowerband_new[-1]):
1246 | g.matrix[index][6] = True
1247 | g.means6 += 1
1248 | else:
1249 | g.matrix[index][6] = False
1250 | flag_xiachuancishu = 0
1251 | # for t in range(1, 80):
1252 | # if flag_xiachuan == 0:
1253 | # if strong_weak[t] > lowerband_new[t]:
1254 | # # 定义标识数
1255 | # flag_xiachuan = 1
1256 | # if flag_xiachuan == 1:
1257 | # if strong_weak[t] <= lowerband_new[t]:
1258 | # flag_xiachuancishu = 1
1259 | # flag_xiachuan = 2
1260 | #
1261 | # if flag_xiachuancishu != 0:
1262 | # # log.info("!=0")
1263 | # # log.info(stock+"触碰布林线下穿线")
1264 | # g.matrix[index][6] = True
1265 | # g.means6+=1
1266 | # else:
1267 | # # log.info("==0")
1268 | # # log.info(stock + "不触碰布林线下穿线")
1269 | # g.matrix[index][6] = False
1270 |
1271 | # trade(context)
1272 |
1273 |
1274 | # #收盘上穿20日BOLL下轨
1275 |
1276 |
1277 | # 日均线穿布林线策略。该函数返回0 1 2 三类数据。0代表 上穿次数大于下 1代表相等 2代表 上穿小于下穿
1278 |
1279 | # #加仓条件
1280 |
1281 | def calcUnit(portfolio_value, ATR):
1282 | value = portfolio_value * 0.01 # trade_percent
1283 | return int((value / ATR) / 100) * 100
1284 |
1285 |
1286 | # 1. 获取上月月末日期
1287 | def func_get_end_date_of_last_month(current_date):
1288 | # 获取从上一个交易日前一个月中的所有交易日,日期排序从前至后
1289 | trade_days = list(get_trade_days(None, current_date, count=30))
1290 |
1291 | # 转化为%Y%m%d格式
1292 | for i in range(len(trade_days)):
1293 | trade_days[i] = trade_days[i].strftime('%Y%m%d')
1294 |
1295 | # 只要交易日的date和当前交易日的月份不同即为上一个月月末日期,例如[20171013]-[20170928]
1296 | # reversed反转序列,便于快速找到月末日期
1297 | for date in reversed(trade_days):
1298 | if date[5] != current_date[5]:
1299 | return date
1300 |
1301 | log.info('找不到上个月末调仓日!')
1302 | return
1303 |
1304 |
1305 | # 获取收益率序列
1306 | def g_rate(sct, days, nd):
1307 | for i, day in enumerate(days):
1308 | if i >= 1:
1309 | prices = \
1310 | get_price(sct, days[i - 1], days[i], '%dd' % nd, ['close'], skip_paused=False, fq='pre', is_panel=1)[
1311 | 'close']
1312 | close_rate = (prices.iloc[-1] - prices.iloc[0]) / prices.iloc[0]
1313 | yield close_rate
1314 |
1315 |
1316 | # 计算一组数据的IC绝对值均值、正向比例、均值/标准差
1317 | def ics_return(l):
1318 | # 信息系数绝对值均值
1319 | a = [abs(i) for i in l]
1320 | avr_ic = sum(a) / len(l)
1321 |
1322 | # 信息比率
1323 | b = np.array(l)
1324 | if b.std() != 0:
1325 | ir = b.mean() / b.std()
1326 | else:
1327 | ir = 0
1328 |
1329 | # 正向比例
1330 | c = [i for i in l if i > 0]
1331 | pr = len(c) / len(l)
1332 | return avr_ic, ir, pr
1333 |
1334 |
1335 | # 求一组数的均值并保留6位小数
1336 | def avr_6(l):
1337 | b = round(np.array(l).mean(), 6)
1338 | return b
1339 |
1340 |
1341 | # 3 sigma 去极值
1342 | def filter_extreme_3sigma(series, n=3):
1343 | # 均值
1344 | mean = series.mean()
1345 |
1346 | # 标准差
1347 | std = series.std()
1348 |
1349 | max_range = mean + n * std
1350 | min_range = mean - n * std
1351 |
1352 | # clip函数用于将超出范围的值填充为min_range,max_range
1353 | return np.clip(series, min_range, max_range)
1354 |
1355 |
1356 | # z-score标准化
1357 | def standardize(series):
1358 | std = series.std()
1359 | mean = series.mean()
1360 |
1361 | return (series - mean) / std
1362 |
1363 |
1364 | # 分组迭代器
1365 | def group(l, s):
1366 | length = len(l)
1367 | for i in range(s):
1368 | que = []
1369 | left = i * (length // s)
1370 | if (i+1) * (length // s) < length:
1371 | right = (i+1) * (length // s)
1372 | else:
1373 | right = length
1374 | for j in l[left:right]:
1375 | que.append(j)
1376 | yield que
1377 |
1378 |
1379 | # 每日运行函数
1380 | # def handle_bar(context, bar_dict):
1381 | # last_date = get_last_datetime().strftime('%Y%m%d')
1382 | # # log.info('上个交易日日期为:' + last_date)
1383 | #
1384 | # if last_date != context.last_date and len(list(context.portfolio.stock_account.positions.keys())) > 0:
1385 | # # 如果不是调仓日且有持仓,择时买入
1386 | # trade_timing(context, bar_dict)
1387 |
1388 |
1389 | # 择时买股函数
1390 | # def trade_timing(context, bar_dict):
1391 | # trade_before_new(context)
1392 | # trade_new(context)
1393 |
--------------------------------------------------------------------------------
/threefactor.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import numpy as np
3 | import datetime
4 |
5 | def init(context):
6 | # 使用智能选股函数设置股票池
7 | get_iwencai('沪深300')
8 | # 上一次调仓期
9 | context.last_date = ''
10 | # 设置最大持股数
11 | context.max_stocks = 10
12 | # 设置调仓周期,每月月末
13 | run_monthly(reallocation,date_rule=-1)
14 |
15 | def reallocation(context, bar_dict):
16 | # 获取上一个交易日的日期
17 | date = get_last_datetime().strftime('%Y%m%d')
18 |
19 | # 获取上个月末调仓日期
20 | context.last_date = func_get_end_date_of_last_month(date)
21 |
22 | log.info('############################## ' + str(date) + ' ###############################')
23 | '''
24 | # 每个调仓日先清仓持有的股票
25 | for security in list(context.portfolio.stock_account.positions.keys()):
26 | order_target(security, 0)
27 | '''
28 | # 首先获得当前日期
29 | time = get_datetime()
30 | date = time.strftime('%Y%m%d')
31 | # 获得股票池列表
32 | sample = context.iwencai_securities
33 | # 创建字典用于存储因子值
34 | df = {'security':[], 1:[], 2:[], 3:[], 'score':[]}
35 |
36 | # 因子选择
37 | for security in sample:
38 | q=query(
39 | profit.roic,# 投资回报率
40 | valuation.pb,# 市净率
41 | valuation.ps_ttm,# 市销率
42 | ).filter(
43 | profit.symbol==security
44 | )
45 |
46 | # 缺失值填充为0
47 | # fillna(0)缺失值填充0
48 | fdmt = get_fundamentals(q, date=date).fillna(0)
49 |
50 | # 判断是否有数据
51 | if (not (fdmt['profit_roic'].empty or
52 | fdmt['valuation_pb'].empty or
53 | fdmt['valuation_ps_ttm'].empty)):
54 | # 计算并填充因子值
55 | df['security'].append(security)
56 | df[1].append(fdmt['profit_roic'][0])# 因子1:投资回报率
57 | df[2].append(fdmt['valuation_pb'][0])# 因子2:市净率
58 | df[3].append(fdmt['valuation_ps_ttm'][0])#因子3:市销率
59 |
60 | for i in range(1, 4):
61 | # 因子极值处理,中位数去极值法
62 | m = np.mean(df[i])
63 | s = np.std(df[i])
64 | for j in range(len(df[i])):
65 | if df[i][j] <= m-3*s:
66 | df[i][j] = m-3*s
67 | if df[i][j] >= m+3*s:
68 | df[i][j] = m+3*s
69 | m = np.mean(df[i])
70 | s = np.std(df[i])
71 |
72 | # 因子无量纲处理,标准化法
73 | for j in range(len(df[i])):
74 | df[i][j] = (df[i][j]-m)/s
75 |
76 | # 计算综合因子得分
77 | for i in range(len(df['security'])):
78 | # 等权重计算(注意因子方向)
79 | s = (df[1][i]-df[2][i]-df[3][i])
80 | df['score'].append(s)
81 |
82 | # 按综合因子得分由大到小排序
83 | df = pd.DataFrame(df).sort_values(by ='score', ascending=False)
84 | '''
85 | # 等权重分配资金
86 | cash = context.portfolio.available_cash/context.max_stocks
87 |
88 | # 买入新调仓股票
89 | for security in df[:context.max_stocks]['security']:
90 | order_target_value(security, cash)
91 | '''
92 | # 买入挑选的股票
93 | func_do_trade(context, bar_dict, df)
94 |
95 | context.last_date = date
96 |
97 | def handle_bar(context, bar_dict):
98 | last_date = get_last_datetime().strftime('%Y%m%d')
99 | if last_date != context.last_date and len(list(context.portfolio.stock_account.positions.keys())) > 0:
100 | # 如果不是调仓日且有持仓,判断止损条件
101 | func_stop_loss(context, bar_dict)
102 |
103 | def func_get_end_date_of_last_month(current_date):
104 | # 获取从上一个交易日前一个月中的所有交易日,日期排序从前至后
105 | trade_days = list(get_trade_days(None, current_date, count=30))
106 |
107 | # 转化为%Y%m%d格式
108 | for i in range(len(trade_days)):
109 | trade_days[i] = trade_days[i].strftime('%Y%m%d')
110 |
111 | # 只要交易日的date和当前交易日的月份不同即为上一个月月末日期,例如[20171013]-[20170928]
112 | # reversed反转序列,便于快速找到月末日期
113 | for date in reversed(trade_days):
114 | if date[5] != current_date[5]:
115 | return date
116 |
117 | log.info('Cannot find the end date of last month.')
118 | return
119 |
120 | #### 4.下单函数 ###################################################################
121 | def func_do_trade(context, bar_dict, df):
122 | # 先清空所有持仓
123 | if len(list(context.portfolio.stock_account.positions.keys())) > 0:
124 | for stock in list(context.portfolio.stock_account.positions.keys()):
125 | order_target(stock, 0)
126 | '''
127 | # 买入前30支股票
128 | for stock in context.selected:
129 | order_target_percent(stock, 1./context.hold_max)
130 | if len(list(context.portfolio.stock_account.positions.keys())) >= context.hold_max:
131 | break
132 | '''
133 | # 等权重分配资金
134 | cash = context.portfolio.available_cash/context.max_stocks
135 |
136 | # 买入新调仓股票
137 | for security in df[:context.max_stocks]['security']:
138 | order_target_value(security, cash)
139 | return
140 |
141 |
142 | #### 5.止损函数 ####################################################################
143 | def func_stop_loss(context, bar_dict):
144 | #获取账户持仓信息
145 | holdstock = list(context.portfolio.stock_account.positions.keys())
146 | if len(holdstock) > 0:
147 | num = -0.1
148 | for stock in holdstock:
149 | close = history(stock,['close'],1,'1d').values
150 | if close/context.portfolio.positions[stock].last_price -1 <= num:
151 | order_target(stock,0)
152 | log.info('股票{}已止损'.format(stock))
153 |
154 |
155 | #获取账户持仓信息
156 | holdstock = list(context.portfolio.stock_account.positions.keys())
157 | if len(holdstock) > 0:
158 | num = - 0.13
159 | T = history('000001.SH',['quote_rate'],7,'1d').values.sum()
160 | if T < num*100:
161 | log.info('上证指数连续三天下跌{}已清仓'.format(T))
162 | for stock in holdstock:
163 | order_target(stock,0)
--------------------------------------------------------------------------------
/touzi.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import numpy as np
3 | import pandas as pd
4 | import math
5 |
6 | # 中位数去极值
7 | def winsorize(df, afactor_name, n=20):
8 | '''
9 | df为DataFrame数据
10 | factor为需要去极值的列名称(str类型即可)
11 | n 为判断极值上下边界的常数
12 | '''
13 |
14 | # 提取该列的数据
15 | ls_raw = np.array(df[afactor_name].values)
16 | # 排序 axis=0,按列排列
17 | ls_raw.sort(axis = 0)
18 | # 获取中位数
19 | # 返回数组元素的中位数
20 | D_M = np.median(ls_raw)
21 |
22 | # 计算离差值
23 | ls_deviation = abs(ls_raw - D_M)
24 | # 排序
25 | ls_deviation.sort(axis = 0)
26 | # 获取离差中位数
27 | D_MAD = np.median(ls_deviation)
28 |
29 | # 将大于中位数n倍离差中位数的值赋为NaN
30 | df.ix[df[afactor_name] >= D_M + n * D_MAD] = None
31 | # 将小于中位数n倍离差中位数的值赋为NaN
32 | df.ix[df[afactor_name] <= D_M - n * D_MAD] = None
33 | return df
34 |
35 | # 因子无量纲处理,标准化处理因子值
36 | def standardize(df, afactor_name):
37 | '''
38 | df为DataFrame数据
39 | factor为因子名称,string格式
40 | '''
41 | df[afactor_name] = (df[afactor_name] - df[afactor_name].mean())/df[afactor_name].std()
42 | return df
43 |
44 | #得到IC、IR等有效因子指标
45 | def get_effectiveness(index, afactor):
46 | index = index
47 | end_date = datetime.date(2018, 4, 30)
48 | holding_period = 20
49 | period_number = 12
50 | quantile_number = 5
51 |
52 | # datetime.timedelta对象代表两个时间之间的时间差
53 | # 获取end_date前两轮周期前的交易开始时间start_date
54 | # 两轮周期即一轮因子检验期,一轮回测期?
55 | start_date = end_date - datetime.timedelta(days = 2 * holding_period * period_number)
56 |
57 | # get_trade_days函数的主要功能获取指定时间段内的交易日
58 | # 获取从start_date开始的第一轮周期的所有日期
59 | # 排序为由start_date至end_date
60 | dates = get_trade_days(start_date, end_date)[- holding_period * period_number - 1:].strftime('%Y%m%d')
61 |
62 | trade_dates = []
63 | for i in range(0, len(dates), holding_period):
64 |
65 | # 获取从start_date开始的第一轮周期的所有日期中的交易日
66 | trade_dates.append(dates[i])
67 |
68 | # 转换为DataFrame数据
69 | tradeDates_df = pd.DataFrame(np.array(trade_dates), columns=['Trade_Dates'])
70 |
71 | # 获取index对应的成分股股票代码
72 | securities = get_index_stocks(index, trade_dates[0])
73 |
74 | q = query(
75 | factor.symbol,
76 | afactor
77 | ).filter(
78 | factor.symbol.in_(securities),
79 | factor.date == trade_dates[0]
80 | )
81 | df = get_factors(q).fillna(0)
82 |
83 | afactor_name = list(afactor)
84 | afactor_name[6] = '_'
85 | afactor_name=''.join(afactor_name)
86 |
87 | afactor_name=afactor_name[7:]
88 |
89 | df=winsorize(df,afactor_name,20).copy()
90 | df=df.dropna()
91 |
92 | # 标准化
93 | df = standardize(df, afactor_name).copy()
94 |
95 | # 获取股票收盘价数据
96 | selected_securities = list(df['factor_symbol'].values)
97 | prices = get_price(selected_securities, trade_dates[0], trade_dates[1], '1d', ['close', 'is_paused'], skip_paused = False, fq = 'pre',is_panel = 0)
98 |
99 | return_list = []
100 | for stock in selected_securities:
101 | # 将持仓周期开始日期停牌的股票收益赋为NaN,便于之后剔除
102 | if prices[stock]['is_paused'][0]:
103 | return_list.append(None)
104 | else:
105 | quote_rate = (prices[stock]['close'][-1] - prices[stock]['close'][0]) / prices[stock]['close'][0]
106 | return_list.append(quote_rate)
107 |
108 | df['returns'] = pd.Series(return_list, index = df.index)
109 | # 剔除开始日期停牌的股票
110 | df = df.dropna()
111 |
112 | sorted_list = df.sort_values([afactor_name], ascending = True).copy()
113 |
114 | n = round(1./quantile_number * len(sorted_list))
115 | long_securities = list(sorted_list['factor_symbol'][:n].values)
116 |
117 | current_return = 0
118 | for stock in long_securities:
119 |
120 | current_return += (prices[stock]['close'][-1] - prices[stock]['close'][0])/ prices[stock]['close'][0]
121 | current_return = current_return / n
122 |
123 | # 储存各期的IC值
124 | factor_ic = []
125 | #用于存储因子各期回报的列表
126 | factor_return = []
127 |
128 | # 遍历调仓日日期
129 | for i in range(1, len(trade_dates)):
130 | securities = get_index_stocks(index, trade_dates[i-1])
131 |
132 | q=query(
133 | factor.symbol,
134 | afactor
135 | ).filter(
136 | factor.symbol.in_(securities),
137 | factor.date==trade_dates[i-1]
138 | )
139 | df = get_factors(q).fillna(0)
140 |
141 | #中位数去极值
142 | df = winsorize(df, afactor_name, 20).copy()
143 | df = df.dropna()
144 |
145 | #标准化
146 | df = standardize(df, afactor_name).copy()
147 |
148 | # 获取股票列表
149 | selected_securities = list(df['factor_symbol'].values)
150 | # 获取股票收盘价数据
151 | prices = get_price(selected_securities, trade_dates[i-1], trade_dates[i], '1d', ['close', 'is_paused'], skip_paused = False, fq = 'pre',is_panel = 0)
152 |
153 | return_list = []
154 | for stock in selected_securities:
155 | # 将持仓周期开始日期停牌的股票收益赋为NaN,便于之后剔除
156 | if prices[stock]['is_paused'][0]:
157 | return_list.append(None)
158 | else:
159 | # 计算股票持仓周期的收益率
160 | quote_rate = (prices[stock]['close'][-1] - prices[stock]['close'][0]) / prices[stock]['close'][0]
161 | return_list.append(quote_rate)
162 |
163 | # 将股票收益添加到DataFrame
164 | df['returns'] = pd.Series(return_list, index = df.index)
165 |
166 | # 剔除开始日期停牌的股票
167 | df = df.dropna()
168 |
169 | # 计算当期IC值
170 | factor_ic.append(df[afactor_name].corr(df['returns']))
171 |
172 | ######################### 计算因子回报 #########################
173 | # 根据因子的值排序
174 | sorted_list = df.sort_values([afactor_name], ascending = True).copy()
175 | n = round(1./quantile_number * len(sorted_list))
176 | long_securities = list(sorted_list['factor_symbol'][:n].values)
177 | current_return = 0
178 | for stock in long_securities:
179 | current_return += (prices[stock]['close'][-1] - prices[stock]['close'][0]) / prices[stock]['close'][0]
180 | current_return = current_return / n
181 | factor_return.append(current_return)
182 | result_df = pd.DataFrame({'Factor_IC': factor_ic,
183 | 'Factor_Return': factor_return}, index=trade_dates[1:])
184 | result_df.index.name = 'Dates'
185 |
186 | factor_ic = np.array(factor_ic)
187 | factor_return = np.array(factor_return)
188 | average_ic = factor_ic.mean()
189 | annual_r = (1 + factor_return.mean()) ** (250./holding_period) - 1
190 | annual_sigma = factor_return.std() * math.sqrt(250./holding_period)
191 | info_ratio = annual_r / annual_sigma
192 | win_rate = len(factor_return[factor_return > 0]) / len(factor_return)
193 | factor_df = pd.DataFrame([average_ic, info_ratio, annual_r, win_rate], index=['IC','IR','Annual_Return','Win_Rate'], columns=[afactor_name])
194 | return factor_df
195 |
196 | # def init(context):是初始化函数 只在整个回测或模拟交易开始运行时执行一次
197 | def init(context):
198 | get_iwencai('沪深300')
199 | context.max_stocks = 10
200 | run_monthly(reallocation, date_rule=1)
201 |
202 | def reallocation(context, bar_dict):
203 | for security in list(context.portfolio.stock_account.positions.keys()):
204 | order_target(security, 0)
205 | time = get_datetime()
206 | date = time.strftime('%Y%m%d')
207 | sample = context.iwencai_securities
208 | df = {'security':[], 1:[],2:[],3:[],4:[],5:[],6:[],7:[],8:[],9:[],10:[], 'score':[]}
209 |
210 | l=[]
211 | for security in sample:
212 | q = query(
213 | factor
214 | ).filter(
215 | factor.symbol == security,
216 | factor.date == date
217 | )
218 | df1 = get_factors(q).fillna(0)
219 | l=df1.columns.values
220 | break
221 |
222 | l_new=[]
223 | for f in l:
224 | string = list(f)
225 | string[6] = '.'
226 | string=''.join(string)
227 | l_new.append(string)
228 |
229 | eff_list=[]
230 | index='000300.SH'
231 | for f in l_new:
232 | if f!='factor.id' and f!='factor.symbol' and f!='factor.date':
233 | try:
234 | eff=get_effectiveness(index, f)
235 | except Exception as e:
236 | continue
237 | else:
238 | eff_list.append(eff)
239 |
240 | chosen_factor=[]
241 | for security in sample:
242 | q = query(
243 | factor.pe
244 | ).filter(
245 | factor.symbol == security,
246 | factor.date == date
247 | )
248 | print(q)
249 | df1 = get_factors(q).fillna(0)
250 | print('df1:',df1)
251 | print('df1.columns.values:',df1.columns.values)
252 |
253 | if (not (df1['factor_pe'].empty)):
254 | df['security'].append(security)
255 | df[1].append(df1['factor_pe'][0])
256 | print('factor_pe的值为:',df1)
257 |
258 |
259 |
260 | for i in range(1, 2):
261 | # 因子极值处理,中位数去极值法
262 | m = np.mean(df[i])
263 | s = np.std(df[i])
264 | for j in range(0,len(df[i])):
265 | #print('df[i][j]:',df[i][j][0])
266 | if df[i][j] <= m-3*s:
267 | df[i][j] = m-3*s
268 | if df[i][j] >= m+3*s:
269 | df[i][j] = m+3*s
270 | m = np.mean(df[i])
271 | s = np.std(df[i])
272 |
273 | # 因子无量纲处理,标准化法
274 | for j in range(len(df[i])):
275 | df[i][j] = (df[i][j]-m)/s
276 |
277 | # 计算综合因子得分
278 | for i in range(len(df['security'])):
279 | s = (df[1][i])
280 | #print('s',s[0])
281 | df['score'].append(s)
282 |
283 | df = pd.DataFrame(df).sort_values(by ='score', ascending=False)
284 | df_security=list(df['security'])
285 | top20_df_security=[]
286 | for i in range(0,20):
287 | top20_df_security.append(df_security[i])
288 | print('前20个:',top20_df_security)
289 | cash = context.portfolio.available_cash/context.max_stocks
290 |
291 | import talib
292 | import talib as ta
293 | import pandas as pd
294 | import numpy as np
295 |
296 | record = pd.DataFrame({'symbol':[],'add_time':[],'last_buy_price':[]} )
297 |
298 | def init(context):
299 | log.info('begin')
300 | get_iwencai('沪深300')
301 | context.stoplossmultipler = 0.95
302 | context.max_stocks = 40
303 | matrix1=[[0 for i in range(10)] for i in range(40)]
304 | g.matrix=matrix1
305 |
306 | def before_trading(context):
307 |
308 |
309 |
310 | if g.matrix[0][0]==0:
311 | for i in range(0,40):
312 | g.matrix[i][0]=context.iwencai_securities[i]
313 | trade_before_new(context)
314 |
315 | def handle_bar(context, bar_dict):
316 | trade_new(context)
317 |
318 | def trade_new(context):
319 | global record
320 | a=get_datetime()
321 |
322 |
323 | current_universe = context.iwencai_securities
324 | security_position = context.portfolio.stock_account.positions.keys()
325 | for index in range(0,40):
326 |
327 |
328 |
329 | stock=g.matrix[index][0] #获得备选列表中的股票代码
330 | sec=stock
331 | curent=get_candle_stick([stock], end_date=a, fre_step='1m', fields=['open', 'close', 'high', 'low', 'volume'], skip_paused=False, fq=None, bar_count=5, is_panel=0)
332 | current_price=curent[stock]['open'][-1]
333 |
334 | value = history([stock], ['close', 'open', 'low', 'turnover', 'high'], 20, '1d', False, None)
335 |
336 |
337 | value1 = history(stock, ['close', 'open', 'low', 'volume','high'], 60, '1d', True, 'pre')
338 | value1 = value1.dropna()
339 | close = value1.close.values
340 | low = value1.low.values
341 | high = value1['high'].as_matrix()
342 | vol = value1['volume'].as_matrix()
343 | current_price = close[-1]
344 | short_ma = ta.MA(close, 5)
345 | long_ma = ta.MA(close, 10)
346 | mid_ma = ta.MA(close, 20)
347 | up, mid, low = talib.BBANDS(close, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0)
348 |
349 | atr = talib.ATR(high, low, close, 20)[-1]
350 |
351 |
352 | upperband, middleband, lowerband = talib.BBANDS(np.asarray(value[stock]['close']), timeperiod=10, nbdevup=1.96, nbdevdn=1.96, matype=0)
353 |
354 | if index<19:
355 |
356 | if ( (g.matrix[index][1]==True and g.matrix[index][3]==True and g.matrix[index][4]==True) or (g.matrix[index][6]==True)) :
357 |
358 |
359 | order_target_value(stock, context.portfolio.available_cash / 20)
360 | if len(record)!=0:
361 | record = record[record['symbol']!=stock]
362 |
363 | record = record.append(pd.DataFrame({'symbol':[stock],'add_time':[1],'last_buy_price':[current_price]}))
364 | continue
365 |
366 |
367 | if index>=20:
368 |
369 | if ( (g.matrix[index][1]==True and g.matrix[index][3]==True and g.matrix[index][4]==True) or (g.matrix[index][6]==False) ) :
370 |
371 | log.info("买小")
372 | log.info(stock)
373 | order_target_value(stock, context.portfolio.available_cash / 10)
374 | if len(record)!=0:
375 | record = record[record['symbol']!=stock]
376 |
377 | record = record.append(pd.DataFrame({'symbol':[stock],'add_time':[1],'last_buy_price':[current_price]}))
378 | continue
379 |
380 |
381 | for t in record['symbol']:
382 | if t ==sec:
383 |
384 | if (record[record['symbol'] == sec]['last_buy_price'].empty):
385 | continue
386 | else:
387 | log.info("进入判断")
388 | last_price = float(record[record['symbol'] == sec]['last_buy_price'])
389 | add_price = last_price + 0.5 * atr
390 | add_unit = float(record[record['symbol'] == sec]['add_time'])
391 | if current_price > add_price*0.5 and add_unit < 4:
392 | log.info("加仓")
393 | unit = calcUnit(context.portfolio.portfolio_value, atr)
394 |
395 | log.info(unit)
396 | order(sec, 2*unit)
397 | record.loc[record['symbol'] == sec, 'add_time'] = record[record['symbol'] == sec][
398 | 'add_time'] + 1
399 | record.loc[record['symbol'] == sec, 'last_buy_price'] = current_price
400 |
401 |
402 | log.info(current_price,last_price)
403 | if current_price < (last_price - 0.3 * atr) :
404 | log.info("抛售")
405 | log.info(sec)
406 | order_target_value(sec, 0)
407 |
408 | record = record[record['symbol'] != sec]
409 |
410 | def trade_before_new(context):
411 | a=get_datetime()
412 | for index in range(0,40):
413 |
414 | stock=g.matrix[index][0]
415 | sec=stock
416 | curent=get_candle_stick([stock], end_date=a, fre_step='1m', fields=['open', 'close', 'high', 'low', 'volume'], skip_paused=False, fq=None, bar_count=5, is_panel=0)
417 |
418 | value = history([stock], ['close', 'open', 'low', 'turnover', 'high'], 20, '1d', False, None)
419 |
420 |
421 | value1 = history(stock, ['close', 'open', 'low', 'volume','high'], 110, '1d', True, 'pre')
422 | value10=history('000300.SH',['close', 'open', 'low', 'volume','high'], 110, '1d', True, 'pre')
423 | log.info(value10)
424 | value11=history('000905.SH',['close', 'open', 'low', 'volume','high'], 110, '1d', True, 'pre')
425 | value1 = value1.dropna()
426 | close = value1.close.values
427 | low = value1.low.values
428 | high = value1['high'].as_matrix()
429 | vol = value1['volume'].as_matrix()
430 |
431 | current_price = close[-1]
432 | short_ma = ta.MA(close, 5)
433 | long_ma = ta.MA(close, 10)
434 | mid_ma = ta.MA(close, 20)
435 | up, mid, low = talib.BBANDS(close, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0)
436 |
437 | atr = talib.ATR(high, low, close, 20)[-1]
438 | upperband, middleband, lowerband = talib.BBANDS(np.asarray(value[stock]['close']), timeperiod=10, nbdevup=1.96, nbdevdn=1.96, matype=0)
439 |
440 | short_vol=ta.MA(vol,5)
441 | long_vol=ta.MA(vol,10)
442 |
443 | if short_vol[-1]>short_vol[-2] and long_vol[-1]>long_vol[-2] and short_vol[-2]<=long_vol[-2] and short_vol[-1]>long_vol[-1]:
444 | g.matrix[index][1]=True
445 | else:
446 | g.matrix[index][1]=False
447 | if (short_vol[-2]>=long_vol[-2] and short_vol[-1]DEA[-1] and DIF[-1]>0 and DEA[-1]>0:
459 | g.matrix[index][3]=True
460 |
461 | else:
462 | g.matrix[index][3]=False
463 |
464 |
465 | if short_ma[-1]>mid_ma[-2] and short_ma[-1]>short_ma[-2] and mid_ma[-1]>mid_ma[-2] and short_ma[-1]>mid_ma[-1] and mid_ma[-1]>long_ma[-1] and short_ma[-2]<=mid_ma[-2] and short_ma[-1]>mid_ma[-1]:
466 | g.matrix[index][4]=True
467 | else:
468 | g.matrix[index][4]=False
469 | if (short_ma[-2]>=mid_ma[-2] and short_ma[-1]upperband_new[t]:
487 | flag_shangchuancishu=flag_shangchuancishu+1
488 | flag_shangchuan=2
489 | if flag_shangchuan==2:
490 | if long_ma[t]>upperband_new[t]:
491 | flag_shangchuan=2
492 | if long_ma[t]lowerband_new[t]:
497 | flag_xiachuan=1
498 | if flag_xiachuan==1:
499 | if long_ma[t]lowerband_new[t]:
506 | flag_xiachuan=0
507 |
508 | if flag_xiachuancishu!=0:
509 |
510 | g.matrix[index][6]=True
511 |
512 |
513 | if flag_xiachuancishu==0:
514 |
515 | g.matrix[index][6]=False
516 |
517 | def request1(value,value1,value2,stock):
518 |
519 | close = value[stock]['close'].as_matrix()
520 |
521 | close1 = value1[stock]['close'].as_matrix()
522 |
523 | close2 = value2[stock]['close'].as_matrix()
524 |
525 | upperband, middleband, lowerband = talib.BBANDS(np.asarray(value2[stock]['close']), timeperiod=70, nbdevup=2, nbdevdn=2, matype=0)
526 |
527 |
528 | ma10=talib.SMA(close1,timeperiod=70)
529 |
530 | open1 = value[stock]['open']
531 | close = value[stock]['close']
532 | low = value[stock]['low']
533 |
534 | cut_ma=ma10[9:109]
535 |
536 | cut_upperband=upperband[19:119]
537 |
538 | cut_middleband=middleband[19:119]
539 | cut_lowerband=lowerband[19:119]
540 | flag_shangchuan=0
541 | flag_xiachuan=0
542 | flag_shangchuancishu=0
543 | flag_xiachuancishu
544 |
545 | def request2(value,stock,index):
546 |
547 | tur = value[stock]['close'].as_matrix()
548 | short_ma = talib.MA(tur, 5)
549 | mid_ma = talib.MA(tur, 10)
550 | long_ma = talib.MA(tur, 10)
551 | if short_ma[-1]>mid_ma[-2] and short_ma[-1]>short_ma[-2] and mid_ma[-1]>mid_ma[-2] and short_ma[-1]>mid_ma[-1] and mid_ma[-1]>long_ma[-1] and short_ma[-2]<=mid_ma[-2] and short_ma[-1]>mid_ma[-1]:
552 |
553 | g.matrix[index][2]=True
554 |
555 | log.info("多头排列True")
556 | else:
557 | g.matrix[index][2]=False
558 | log.info("多头排列False")
559 | if (short_ma[-2]>=mid_ma[-2] and short_ma[-1]short_vol[-2] and long_vol[-1]>long_vol[-2] and short_vol[-2]<=long_vol[-2] and short_vol[-1]>long_vol[-1]:
575 | g.matrix[index][4]=True
576 |
577 | else:
578 | g.matrix[index][4]=False
579 |
580 | if (short_vol[-2]>=long_vol[-2] and short_vol[-1] DEA[-1] and DIF[-1] > 0 and DEA[-1] > 0:
590 |
591 | g.matrix[index][6]=True
592 | log.info("MACD成功")
593 | else:
594 | g.matrix[index][6]=False
595 |
596 | def add_request(value,stock):
597 | high = value['high'].as_matrix()
598 | low = value['low'].as_matrix()
599 | close = value['close'].as_matrix()
600 | atr = talib.ATR(high, low, close, 20)[-1]
601 |
602 | if (record[record['symbol'] == stock]['last_buy_price'].empty):
603 | return False
604 | else:
605 | last_price = float(record[record['symbol'] == stock]['last_buy_price'])
606 | add_price = last_price + 0.5 * atr
607 | add_unit = float(record[record['symbol'] == stock]['add_time'])
608 | if close[-1] > add_price and add_unit <2:
609 | return True
610 | else:
611 | return False
612 | def calcUnit(portfolio_value,ATR):
613 |
614 | value = portfolio_value * 0.01
615 | return int((value/ATR)/100)*100
616 |
--------------------------------------------------------------------------------
/touzi2.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import numpy as np
3 | import datetime, time
4 | from dateutil.relativedelta import relativedelta
5 | import talib
6 | import math, random
7 | from scipy.stats import ttest_ind
8 | from scipy.stats import levene
9 | from sklearn.linear_model import LinearRegression
10 |
11 | def init(context):
12 |
13 | # 最大持股数量(大小盘各一半)
14 | context.hold_max = 40
15 |
16 | # 初始因子池(包括技术因子和财务因子在内的共计107个初始因子)
17 | context.need=['factor.pe',# 市盈率
18 | 'factor.pb',# 市净率
19 | 'factor.pcf_cash_flow_ttm',# 市现率PCF
20 | 'factor.ps',# 市销率
21 | 'factor.dividend_rate',# 股息率
22 | 'factor.market_cap',# 总市值
23 | 'factor.current_market_cap',# 流通市值
24 | 'factor.capitalization',# 总股本
25 | 'factor.circulating_cap',# 流通股本
26 | 'factor.current_ratio',# 流动比率
27 | 'factor.equity_ratio',# 产权比率负债合计/归属母公司股东的权益
28 | 'factor.quick_ratio',# 速动比率
29 | 'factor.tangible_assets_liabilities',# 有形资产/负债合计
30 | 'factor.tangible_assets_int_liabilities',# 有形资产/带息债务
31 | 'factor.net_debt_equity',# 净债务/股权价值
32 | 'factor.long_term_debt_to_opt_capital_ratio',# 长期债务与营运资金比率
33 | 'factor.tangible_assets_net_liabilities',# 有形资产/净债务
34 | 'factor.overall_income_growth_ratio',# 营业总收入同比增长率
35 | # 'factor.net_cashflow_psg_rowth_ratio',# 每股经营活动产生的现金流量净额同比增长率(报错,原因询问客服ing)
36 | # 'factor.opt_profit_grow_ratio',# 营业利润同比增长率
37 | # 'factor.total_profit_growth_ratio',# 利润总额同比增长率
38 | 'factor.diluted_net_asset_growth_ratio',# 净资产收益率摊薄同比增长率
39 | 'factor.net_cashflow_from_opt_act_growth_ratio',# 经营活动产生的现金流量净额同比增长率
40 | 'factor.net_profit_growth_ratio',# 净利润同比增长率
41 | # 'factor.basic_pey_ear_growth_ratio',# 基本每股收益同比增长率
42 | 'factor.turnover_of_overall_assets',# 总资产周转率
43 | 'factor.turnover_ratio_of_account_payable',# 应付账款周转率
44 | 'factor.turnover_of_current_assets',# 流动资产周转率
45 | 'factor.turnover_of_fixed_assets',# 固定资产周转率
46 | 'factor.cash_cycle',# 现金循环周期
47 | 'factor.inventory_turnover_ratio',# 存货周转率
48 | 'factor.turnover_ratio_of_receivable',# 应收账款周转率
49 | 'factor.weighted_roe',# 净资产收益率roe加权
50 | 'factor.overall_assets_net_income_ratio',# 总资产净利率roa
51 | 'factor.net_profit_margin_on_sales',# 销售净利率
52 | 'factor.before_tax_profit_div_income',# 息税前利润/营业总收入
53 | 'factor.sale_cost_div_income',# 销售费用/营业总收入
54 | 'factor.roa',# 总资产报酬率roa
55 | 'factor.ratio_of_sales_to_cost',# 销售成本率
56 | 'factor.net_profit_div_income',# 净利润/营业总收入
57 | 'factor.opt_profit_div_income',# 营业利润/营业总收入
58 | 'factor.opt_cost_div_income',# 营业总成本/营业总收入
59 | 'factor.administration_cost_div_income',# 管理费用/营业总收入
60 | 'factor.financing_cost_div_income',# 财务费用/营业总收入
61 | 'factor.impairment_loss_div_income',# 资产减值损失/营业总收
62 | # 以下为技术因子
63 | 'factor.vr_rate',# 成交量比率
64 | 'factor.vstd',# 成交量标准差
65 | 'factor.arbr',# 人气意愿指标
66 | 'factor.srdm',# 动向速度比率
67 | 'factor.vroc',# 量变动速率
68 | 'factor.vrsi',# 量相对强弱指标
69 | 'factor.cr',# 能量指标
70 | 'factor.mfi',# 资金流向指标
71 | 'factor.vr',# 量比
72 | 'factor.mass',# 梅丝线
73 | 'factor.obv',# 能量潮
74 | 'factor.pvt',# 量价趋势指标
75 | 'factor.wad',# 威廉聚散指标
76 | 'factor.bbi',# 多空指数
77 | 'factor.mtm',# 动力指标
78 | 'factor.dma',# 平均线差
79 | 'factor.ma',# 简单移动平均
80 | 'factor.macd',# 指数平滑异同平均
81 | 'factor.expma',# 指数平均数
82 | 'factor.priceosc',# 价格振荡指标
83 | 'factor.trix',# 三重指数平滑平均
84 | 'factor.dbcd',# 异同离差乖离率
85 | 'factor.dpo',# 区间震荡线
86 | 'factor.psy',# 心理指标
87 | 'factor.vma',# 量简单移动平均
88 | 'factor.vmacd',# 量指数平滑异同平均
89 | 'factor.vosc',# 成交量震荡
90 | 'factor.tapi',# 加权指数成交值
91 | 'factor.micd',# 异同离差动力指数
92 | 'factor.rccd',# 异同离差变化率指数
93 | 'factor.ddi',# 方向标准差偏离指数
94 | 'factor.bias',# 乖离率
95 | 'factor.cci',# 顺势指标
96 | 'factor.kdj',# 随机指标
97 | 'factor.lwr',# L威廉指标
98 | 'factor.roc',# 变动速率
99 | 'factor.rsi',# 相对强弱指标
100 | 'factor.si',# 摆动指标
101 | 'factor.wr',# 威廉指标
102 | 'factor.wvad',# 威廉变异离散量
103 | 'factor.bbiboll',# BBI多空布林线
104 | 'factor.cdp',# 逆势操作
105 | 'factor.env',# ENV指标
106 | 'factor.mike',# 麦克指标
107 | 'factor.adtm',# 动态买卖气指标
108 | 'factor.mi',# 动量指标
109 | 'factor.rc',# 变化率指数
110 | 'factor.srmi',# SRMIMI修正指标
111 | 'factor.dptb',# 大盘同步指标
112 | 'factor.jdqs',# 阶段强势指标
113 | 'factor.jdrs',# 阶段弱势指标
114 | 'factor.zdzb',# 筑底指标
115 | 'factor.atr',# 真实波幅
116 | 'factor.std',# 标准差
117 | 'factor.vhf',# 纵横指标
118 | 'factor.cvlt']# 佳庆离散指标
119 |
120 | # 最大因子数目
121 | context.need_max = 10
122 |
123 | # 上一次调仓期
124 | context.last_date = ''
125 |
126 | # 设置调仓周期,每月倒数第一个交易日运行
127 | run_monthly(func = reallocate, date_rule = -1)
128 |
129 | # 设置因子测试周期为12个月
130 | context.need_tmonth = 12
131 |
132 | # 选择沪深300
133 | get_iwencai('沪深300', 'hs_300')
134 |
135 | # 选择中证500
136 | get_iwencai('中证500', 'zz_500')
137 |
138 | def reallocate(context, bar_dict):
139 | # 获取上一个交易日的日期
140 | date = get_last_datetime().strftime('%Y%m%d')
141 | log.info('上个交易日日期为:' + date)
142 |
143 | # 获取上个月末调仓日期
144 | context.last_date = func_get_end_date_of_last_month(date)
145 |
146 | log.info('上月月末调仓日期为:' + context.last_date)
147 |
148 | hs_needs, zz_needs = get_needs(context, bar_dict, date, context.hs_300), get_needs(context, bar_dict, date, context.zz_500)
149 | # log.info('这是沪深300股票池的因子:')
150 | # log.info(hs_needs)
151 |
152 | # log.info('这是中证500股票池的因子:')
153 | # log.info(zz_needs)
154 | # 用来存放大小盘股票,0是小盘,1是大盘
155 | tempstocks = [[], []]
156 | tempstocks[0].append(get_stocks(context, bar_dict, zz_needs, date, context.zz_500))
157 | tempstocks[1].append(get_stocks(context, bar_dict, hs_needs, date, context.hs_300))
158 | '''
159 | tempstocks = []
160 | tempstocks.append(get_stocks(context, bar_dict, zz_needs, date, context.zz_500))
161 | tempstocks.append(get_stocks(context, bar_dict, hs_needs, date, context.hs_300))
162 | log.info(tempstocks)
163 |
164 | func_do_trade(context, bar_dict, tempstocks)
165 | '''
166 |
167 | # 获得因子函数
168 | def get_needs(context, bar_dict, date, stocks):
169 | time_tuple = time.strptime(date, '%Y%m%d')
170 | year, month, day = time_tuple[:3]
171 | # 转化为datetime.date类型
172 | date = datetime.date(year, month, day)
173 |
174 | last_year_date =(date - relativedelta(years = 1)).strftime('%Y%m%d')
175 | log.info('上个交易日前一年的日期为:' + last_year_date)
176 |
177 | # 上一年内的所有交易日期
178 | trade_days = get_trade_days(last_year_date, date).strftime('%Y-%m-%d')
179 |
180 | # 所有因子
181 | need_all = ','.join(context.need)
182 |
183 | # 1.分组单因子有效性检验,参考资料:https://www.ricequant.com/community/topic/702/%E5%8D%95%E4%B8%80%E5%9B%A0%E5%AD%90%E6%9C%89%E6%95%88%E6%80%A7%E6%A3%80%E6%B5%8B/2
184 | # 每隔25天(间隔越短,收益就会越随机)
185 | period = 25
186 |
187 | # 有效因子列表
188 | eff_need = []
189 |
190 | # 每组股票数10%只股票
191 | num = math.ceil(0.1 * len(stocks))
192 |
193 | # 获取开始日期因子值
194 | q = query(
195 | factor.symbol,
196 | need_all
197 | ).filter(
198 | factor.symbol.in_(stocks),
199 | # 20180430的数据消失,原因是:last_year_date可能不是交易日,这样就获取不到数据,因此为trade_days[0]
200 | factor.date == trade_days[0]
201 | )
202 |
203 | df = get_factors(q)
204 |
205 | for col in df.columns.values[1:]:
206 | # 分别按正序和倒序提取因子值前10%和后10%的股票
207 | a_hign = df.sort_values(col, axis=0, ascending=False)
208 | a_low = df.sort_values(col, axis=0, ascending=True)
209 |
210 | hign = a_hign.iloc[0:num, 0].values.tolist()
211 | low = a_low.iloc[0:num, 0].values.tolist()
212 | hign_price = get_price(hign, last_year_date, date, '1d', ['close'], skip_paused = False, fq = 'pre', is_panel = 1)
213 | low_price = get_price(low, last_year_date, date, '1d', ['close'], skip_paused = False, fq = 'pre', is_panel = 1)
214 |
215 | hign_price = hign_price['close']
216 | low_price = low_price['close']
217 |
218 | # 计算平均每组股票每日价格
219 | high_series = hign_price.T.mean()
220 | low_series = low_price.T.mean()
221 |
222 | # 计算收益率
223 | hign_rr = high_series.pct_change(period).dropna()
224 | low_rr = low_series.pct_change(period).dropna()
225 |
226 | # 判断两组数据的方差是否显著不同
227 | le = levene(hign_rr, low_rr)
228 |
229 | # 判断两组数据的均值是否显著不同
230 | tt = ttest_ind(hign_rr, low_rr, equal_var = False)
231 |
232 | # 取置信度为0.05
233 | if le.pvalue < 0.05 and tt.pvalue < 0.05:
234 | eff_need.append('factor.%s' % col)
235 |
236 | # 第一步有效因子个数
237 | log.info(len(eff_need))
238 |
239 | effneed = ','.join(eff_need)
240 |
241 | # 第二步,股票池股票分十档计算IC、IR、根据IC特征设定方向,根据每组IC均值设定因子权重
242 |
243 | # gp = 2
244 | q = query(
245 | factor.symbol,
246 | effneed
247 | ).filter(
248 | factor.symbol.in_(stocks),
249 | factor.date == trade_days[0]
250 | )
251 |
252 | df = get_factors(q)
253 |
254 | # step档
255 | step = 10
256 |
257 | # 周期
258 | nd = 22
259 |
260 | # 用来存放各指标值
261 | need_data = {}
262 |
263 | # 循环每一个因子
264 | for col in df.columns.values[1:]:
265 | # 因子值从大到小排列
266 | a_hign = df.sort_values(col, axis=0, ascending=False)
267 | hign = a_hign.iloc[:, 0].values.tolist()
268 |
269 | # 用来存放step组数据IC均值、ic、pr值列表
270 | avr = [[], [], []]
271 |
272 | # 对所取两组进行迭代(gp_random:stocks为股票池, gp为取gp组,num为每组num只股票)
273 | # group()将一个list分为step组
274 | for sct in group(list(hign), step):
275 |
276 | # 用来存放一组内每期ic值
277 | ics = []
278 |
279 | # 每个分期时间点
280 | days = trade_days[0:len(trade_days):nd]
281 |
282 | for i, rate in enumerate(g_rate(sct, days, nd)):
283 | q = query(
284 | col
285 | ).filter(
286 | factor.symbol.in_(sct),
287 | factor.date == days[i]
288 | )
289 |
290 | df_sct = get_factors(q).fillna(0)
291 |
292 | # col = df_sct.columns.values[0]
293 | df_sct[col] = standardize(filter_extreme_3sigma(df_sct[col])).fillna(0)
294 |
295 | # ic值
296 | ic = pd.Series(df_sct[col]).corr(pd.Series(list(rate)))
297 |
298 | ics.append(ic)
299 |
300 |
301 | # 一组内三个指标
302 | avr_ic, ir, pr = ics_return(ics)
303 | avr[0].append(avr_ic)
304 | avr[1].append(ir)
305 | avr[2].append(pr)
306 |
307 | need_data[col] = {
308 | 'avr_ic' : avr_6(avr[0]),
309 | 'avr_ir' : avr_6(avr[1]),
310 | 'avr_pr' : avr_6(avr[2])
311 | }
312 |
313 | # 对因子进行排序打分
314 | # 存放最终因子
315 | f_needs = {}
316 |
317 | data = pd.DataFrame(need_data).T.dropna()
318 |
319 | # 按照avr_ir降序打分
320 | data = data.sort_values('avr_ic', axis=0, ascending=False)
321 | data['avr_ic_score'] = list(reversed(range(len(data))))
322 |
323 | # 按照avr_ir降打分
324 | data = data.sort_values('avr_ir', axis=0, ascending=False)
325 | data['avr_ir_score'] = list(range(len(data)))
326 |
327 | # 分数相加
328 | data['total_socre'] = data['avr_ic_score'] + data['avr_ir_score']
329 | data = data.sort_values('total_socre', axis=0, ascending=False)
330 |
331 | # 获取排序后的因子
332 | fneeds = data._stat_axis.values.tolist()
333 | if len(fneeds) >= context.need_max:
334 | fneeds = fneeds[0:context.need_max]
335 |
336 | # 获取因子权重
337 | for i in fneeds:
338 | weight = data.loc[i, 'avr_ic']
339 |
340 | if data.loc[i, 'avr_pr'] > 0.5:
341 | weight = - weight
342 |
343 | f_needs['factor.%s' % i] = {
344 | 'weight' : weight
345 | }
346 |
347 | return f_needs
348 |
349 | # 获得股票函数
350 | def get_stocks(context, bar_dict, needs, date, stocks):
351 | # needs为如下字典:
352 | # 例{'factor.dbcd': {'weight': -0.146216}, 'factor.weighted_roe': {'weight': 0.108531}, 'factor.turnover_of_current_assets': {'weight': -0.111679}, 'factor.turnover_of_overall_assets': {'weight': -0.148806}, 'factor.administration_cost_div_income': {'weight': 0.118929}}
353 | need_all = ','.join(list(needs.keys()))
354 | q = query(
355 | factor.symbol,
356 | need_all
357 | ).filter(
358 | factor.symbol.in_(stocks),
359 | factor.date == date
360 | )
361 | df = get_factors(q).fillna(0)
362 |
363 | # 用来存放股票分值
364 | score = []
365 |
366 | weight = [needs[i]['weight'] for i in needs]
367 |
368 | for i in range(len(df)):
369 | result = sum(list(map(lambda x,y:x*y,list(df.iloc[i, 1:]), weight)))
370 | score.append(result)
371 |
372 | df['score'] = score
373 |
374 | df = df.sort_values('score', axis=0, ascending = False)
375 |
376 | # 大小盘默认持股数
377 | hold = int(context.hold_max / 2)
378 |
379 | # 存放选取的股票
380 | stocks_c = list(df.iloc[0 : hold, 0])
381 |
382 | return stocks_c
383 |
384 | # 1. 获取上月月末日期
385 | def func_get_end_date_of_last_month(current_date):
386 | # 获取从上一个交易日前一个月中的所有交易日,日期排序从前至后
387 | trade_days = list(get_trade_days(None, current_date, count=30))
388 |
389 | # 转化为%Y%m%d格式
390 | for i in range(len(trade_days)):
391 | trade_days[i] = trade_days[i].strftime('%Y%m%d')
392 |
393 | # 只要交易日的date和当前交易日的月份不同即为上一个月月末日期,例如[20171013]-[20170928]
394 | # reversed反转序列,便于快速找到月末日期
395 | for date in reversed(trade_days):
396 | if date[5] != current_date[5]:
397 | return date
398 |
399 | log.info('找不到上个月末调仓日!')
400 | return
401 |
402 | # 获取收益率序列
403 | def g_rate(sct, days, nd):
404 | for i, day in enumerate(days):
405 | if i >= 1:
406 | prices = get_price(sct, days[i-1], days[i], '%dd' % nd, ['close'], skip_paused = False, fq = 'pre',is_panel = 1)['close']
407 | close_rate = (prices.iloc[-1] - prices.iloc[0]) / prices.iloc[0]
408 | yield close_rate
409 |
410 | # 计算一组数据的IC绝对值均值、正向比例、均值/标准差
411 | def ics_return(l):
412 |
413 | # 信息系数绝对值均值
414 | a = [abs(i) for i in l]
415 | avr_ic = sum(a) / len(l)
416 |
417 | # 信息比率
418 | b = np.array(l)
419 | if b.std() != 0:
420 | ir = b.mean() / b.std()
421 | else:
422 | ir = 0
423 |
424 | # 正向比例
425 | c = [i for i in l if i > 0]
426 | pr = len(c) / len(l)
427 | return avr_ic, ir, pr
428 |
429 | # 求一组数的均值并保留6位小数
430 | def avr_6(l):
431 | b = round(np.array(l).mean(), 6)
432 | return b
433 |
434 | # 3 sigma 去极值
435 | def filter_extreme_3sigma(series, n=3):
436 | # 均值
437 | mean = series.mean()
438 |
439 | # 标准差
440 | std = series.std()
441 |
442 | max_range = mean + n*std
443 | min_range = mean - n*std
444 |
445 | # clip函数用于将超出范围的值填充为min_range,max_range
446 | return np.clip(series, min_range, max_range)
447 |
448 | # z-score标准化
449 | def standardize(series):
450 | std = series.std()
451 | mean = series.mean()
452 |
453 | return (series - mean) / std
454 |
455 | # 分组迭代器
456 | def group(l, s):
457 | length = len(l)
458 | for i in range(s):
459 | que = []
460 | left = i * (length // s)
461 | if (i+1) * (length // s) < length:
462 | right = (i+1) * (length // s)
463 | else:
464 | right = length
465 | for j in l[left:right]:
466 | que.append(j)
467 | yield que
468 |
469 | # 每日运行函数
470 | def handle_bar(context, bar_dict):
471 | last_date = get_last_datetime().strftime('%Y%m%d')
472 | # log.info('上个交易日日期为:' + last_date)
473 |
474 | if last_date != context.last_date and len(list(context.portfolio.stock_account.positions.keys())) > 0:
475 | # 如果不是调仓日且有持仓,择时买入
476 | func_stop_loss(context, bar_dict)
477 |
478 |
479 | def func_stop_loss(context, bar_dict):
480 | #获取账户持仓信息
481 | holdstock = list(context.portfolio.stock_account.positions.keys())
482 | if len(holdstock) > 0:
483 | num = -0.1
484 | for stock in holdstock:
485 | close = history(stock,['close'],1,'1d').values
486 | if close/context.portfolio.positions[stock].last_price -1 <= num:
487 | order_target(stock,0)
488 | log.info('股票{}已止损'.format(stock))
489 |
490 | #获取账户持仓信息
491 | holdstock = list(context.portfolio.stock_account.positions.keys())
492 | if len(holdstock) > 0:
493 | num = - 0.13
494 | T = history('000001.SH',['quote_rate'],7,'1d').values.sum()
495 | if T < num*100:
496 | log.info('上证指数连续三天下跌{}已清仓'.format(T))
497 | for stock in holdstock:
498 | order_target(stock,0)
499 |
500 | # 4.下单函数
501 | def func_do_trade(context, bar_dict, tempstocks):
502 | # 先清空所有持仓
503 | if len(list(context.portfolio.stock_account.positions.keys())) > 0:
504 | for stock in list(context.portfolio.stock_account.positions.keys()):
505 | order_target(stock, 0)
506 | # 等权重分配资金
507 | cash = context.portfolio.available_cash/context.max_stocks
508 |
509 | # 买入新调仓股票
510 | for security in tempstocks:
511 | order_target_value(security, cash)
512 | return
--------------------------------------------------------------------------------
/touzi3.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import numpy as np
3 | import datetime, time
4 | from dateutil.relativedelta import relativedelta
5 | import talib
6 | import talib as ta
7 | import math, random
8 | from scipy.stats import ttest_ind
9 | from scipy.stats import levene
10 | from sklearn.linear_model import LinearRegression
11 | import copy
12 |
13 | g.record = pd.DataFrame({'symbol': [], 'add_time': [], 'last_buy_price': []})
14 |
15 |
16 | def init(context):
17 | g.num = 0
18 | log.info('begin')
19 | # get_iwencai('沪深300')
20 | # set_benchmark('000905.SH')
21 | # 设置最大持股数
22 | context.stoplossmultipler = 0.95
23 | # 设置交易股票数量
24 | context.max_stocks = 40
25 | matrix1 = [[0 for i in range(10)]]
26 | g.matrix = matrix1
27 | # 用于计数
28 | g.means1 = g.means2 = g.means3 = g.means4 = g.means5 = g.means6 = g.means7 = 0
29 |
30 | # 最大持股数量(大小盘各一半)
31 | context.hold_max = 40
32 |
33 | # 初始因子池(包括技术因子和财务因子在内的共计107个初始因子)
34 | context.need = ['factor.pe', # 市盈率
35 | 'factor.pb', # 市净率
36 | 'factor.pcf_cash_flow_ttm', # 市现率PCF
37 | 'factor.ps', # 市销率
38 | 'factor.dividend_rate', # 股息率
39 | 'factor.market_cap', # 总市值
40 | 'factor.current_market_cap', # 流通市值
41 | 'factor.capitalization', # 总股本
42 | 'factor.circulating_cap', # 流通股本
43 | 'factor.current_ratio', # 流动比率
44 | 'factor.equity_ratio', # 产权比率负债合计/归属母公司股东的权益
45 | 'factor.quick_ratio', # 速动比率
46 | 'factor.tangible_assets_liabilities', # 有形资产/负债合计
47 | 'factor.tangible_assets_int_liabilities', # 有形资产/带息债务
48 | 'factor.net_debt_equity', # 净债务/股权价值
49 | 'factor.long_term_debt_to_opt_capital_ratio', # 长期债务与营运资金比率
50 | 'factor.tangible_assets_net_liabilities', # 有形资产/净债务
51 | 'factor.overall_income_growth_ratio', # 营业总收入同比增长率
52 | # 'factor.net_cashflow_psg_rowth_ratio',# 每股经营活动产生的现金流量净额同比增长率(报错,原因询问客服ing)
53 | # 'factor.opt_profit_grow_ratio',# 营业利润同比增长率
54 | # 'factor.total_profit_growth_ratio',# 利润总额同比增长率
55 | 'factor.diluted_net_asset_growth_ratio', # 净资产收益率摊薄同比增长率
56 | 'factor.net_cashflow_from_opt_act_growth_ratio', # 经营活动产生的现金流量净额同比增长率
57 | 'factor.net_profit_growth_ratio', # 净利润同比增长率
58 | # 'factor.basic_pey_ear_growth_ratio',# 基本每股收益同比增长率
59 | 'factor.turnover_of_overall_assets', # 总资产周转率
60 | 'factor.turnover_ratio_of_account_payable', # 应付账款周转率
61 | 'factor.turnover_of_current_assets', # 流动资产周转率
62 | 'factor.turnover_of_fixed_assets', # 固定资产周转率
63 | 'factor.cash_cycle', # 现金循环周期
64 | 'factor.inventory_turnover_ratio', # 存货周转率
65 | 'factor.turnover_ratio_of_receivable', # 应收账款周转率
66 | 'factor.weighted_roe', # 净资产收益率roe加权
67 | 'factor.overall_assets_net_income_ratio', # 总资产净利率roa
68 | 'factor.net_profit_margin_on_sales', # 销售净利率
69 | 'factor.before_tax_profit_div_income', # 息税前利润/营业总收入
70 | 'factor.sale_cost_div_income', # 销售费用/营业总收入
71 | 'factor.roa', # 总资产报酬率roa
72 | 'factor.ratio_of_sales_to_cost', # 销售成本率
73 | 'factor.net_profit_div_income', # 净利润/营业总收入
74 | 'factor.opt_profit_div_income', # 营业利润/营业总收入
75 | 'factor.opt_cost_div_income', # 营业总成本/营业总收入
76 | 'factor.administration_cost_div_income', # 管理费用/营业总收入
77 | 'factor.financing_cost_div_income', # 财务费用/营业总收入
78 | 'factor.impairment_loss_div_income', # 资产减值损失/营业总收
79 | # 以下为技术因子
80 | 'factor.vr_rate', # 成交量比率
81 | 'factor.vstd', # 成交量标准差
82 | 'factor.arbr', # 人气意愿指标
83 | 'factor.srdm', # 动向速度比率
84 | 'factor.vroc', # 量变动速率
85 | 'factor.vrsi', # 量相对强弱指标
86 | 'factor.cr', # 能量指标
87 | 'factor.mfi', # 资金流向指标
88 | 'factor.vr', # 量比
89 | 'factor.mass', # 梅丝线
90 | 'factor.obv', # 能量潮
91 | 'factor.pvt', # 量价趋势指标
92 | 'factor.wad', # 威廉聚散指标
93 | 'factor.bbi', # 多空指数
94 | 'factor.mtm', # 动力指标
95 | 'factor.dma', # 平均线差
96 | 'factor.ma', # 简单移动平均
97 | 'factor.macd', # 指数平滑异同平均
98 | 'factor.expma', # 指数平均数
99 | 'factor.priceosc', # 价格振荡指标
100 | 'factor.trix', # 三重指数平滑平均
101 | 'factor.dbcd', # 异同离差乖离率
102 | 'factor.dpo', # 区间震荡线
103 | 'factor.psy', # 心理指标
104 | 'factor.vma', # 量简单移动平均
105 | 'factor.vmacd', # 量指数平滑异同平均
106 | 'factor.vosc', # 成交量震荡
107 | 'factor.tapi', # 加权指数成交值
108 | 'factor.micd', # 异同离差动力指数
109 | 'factor.rccd', # 异同离差变化率指数
110 | 'factor.ddi', # 方向标准差偏离指数
111 | 'factor.bias', # 乖离率
112 | 'factor.cci', # 顺势指标
113 | 'factor.kdj', # 随机指标
114 | 'factor.lwr', # L威廉指标
115 | 'factor.roc', # 变动速率
116 | 'factor.rsi', # 相对强弱指标
117 | 'factor.si', # 摆动指标
118 | 'factor.wr', # 威廉指标
119 | 'factor.wvad', # 威廉变异离散量
120 | 'factor.bbiboll', # BBI多空布林线
121 | 'factor.cdp', # 逆势操作
122 | 'factor.env', # ENV指标
123 | 'factor.mike', # 麦克指标
124 | 'factor.adtm', # 动态买卖气指标
125 | 'factor.mi', # 动量指标
126 | 'factor.rc', # 变化率指数
127 | 'factor.srmi', # SRMIMI修正指标
128 | 'factor.dptb', # 大盘同步指标
129 | 'factor.jdqs', # 阶段强势指标
130 | 'factor.jdrs', # 阶段弱势指标
131 | 'factor.zdzb', # 筑底指标
132 | 'factor.atr', # 真实波幅
133 | 'factor.std', # 标准差
134 | 'factor.vhf', # 纵横指标
135 | 'factor.cvlt'] # 佳庆离散指标
136 |
137 | # 最大因子数目
138 | context.need_max = 10
139 |
140 | # 上一次调仓期
141 | context.last_date = ''
142 | # 设置调仓周期,每月倒数第一个交易日运行
143 | run_monthly(func=reallocate, date_rule=1)
144 |
145 | # 设置因子测试周期为12个月
146 | context.need_tmonth = 12
147 |
148 | # 选择沪深300
149 | get_iwencai('沪深300', 'hs_300')
150 |
151 | # 选择中证500
152 | get_iwencai('中证500', 'zz_500')
153 |
154 |
155 | def reallocate(context, bar_dict):
156 | log.info(
157 | "VOL5上穿VOL10次数:{},OL5下穿VOL10:{},DIF{},MA5上穿MA10:{},MA5下穿MA10:{},布林线:{}".format(g.means1, g.means2, g.means3,
158 | g.means4, g.means5, g.means6))
159 | # 获取上一个交易日的日期
160 | date = get_last_datetime().strftime('%Y%m%d')
161 | log.info('上个交易日日期为:' + date)
162 |
163 | # 获取上个月末调仓日期
164 | context.last_date = func_get_end_date_of_last_month(date)
165 |
166 | log.info('上月月末调仓日期为:' + context.last_date)
167 |
168 | hs_needs, zz_needs = get_needs(context, bar_dict, date, context.hs_300), get_needs(context, bar_dict, date,
169 | context.zz_500)
170 | log.info('这是沪深300股票池的因子:')
171 | log.info(hs_needs)
172 |
173 | log.info('这是中证500股票池的因子:')
174 | log.info(zz_needs)
175 |
176 | # 用来存放大小盘股票,0是小盘,1是大盘
177 | tempstocks = [[], []]
178 | dapan_stocks = get_stocks(context, bar_dict, zz_needs, date, context.zz_500)
179 | for stock in dapan_stocks:
180 | tempstocks[0].append(stock)
181 | tempstocks[1].append(1)
182 | xiaopan_stocks = get_stocks(context, bar_dict, hs_needs, date, context.zz_500)
183 | for stock in xiaopan_stocks:
184 | tempstocks[0].append(stock)
185 | tempstocks[1].append(0)
186 | # log.info(tempstocks)
187 | log.info('这是沪深300股票池:')
188 | log.info(dapan_stocks)
189 |
190 | log.info('这是中证500股票池:')
191 | log.info(xiaopan_stocks)
192 | # unit = calcUnit(context.portfolio.portfolio_value, atr) # 计算加仓时应购入的股票数量
193 | # for s in tempstocks[0]:
194 | # order(s, 1 * 2000) # 七个0时约2000
195 | log.info("股票池:")
196 | log.info(tempstocks[0])
197 | # 第一个月
198 | if g.matrix[0][0] == 0:
199 | # log.info("第一个月")
200 | # log.info(g.matrix)
201 | g.matrix.pop(0) # 将此股票从数组中删除
202 | # log.info(g.matrix)
203 | for num, tempstock in enumerate(tempstocks[0]):
204 | matrix2 = [0 for i in range(10)]
205 | matrix2[0] = tempstock
206 | # 1是大盘 0是小盘
207 | matrix2[8] = tempstocks[1][num]
208 | matrix2[9] = 1
209 | matrix3 = copy.deepcopy(matrix2)
210 | g.matrix.append(matrix3)
211 | log.info(g.matrix)
212 | else:
213 | # 先清除标记
214 | log.info("开始新一轮的选股")
215 | log.info(g.matrix)
216 | for n, s in enumerate(g.matrix):
217 | for i in range(0, 9):
218 | if i == 0 or i == 8:
219 | continue
220 | g.matrix[n][i] = 0
221 | log.info(g.matrix)
222 | # 需要比较是否与前一个月重复
223 | for num, tempstock in enumerate(tempstocks[0]):
224 | flag_repeat = 0
225 | for n, stock in enumerate(g.matrix):
226 | if stock[0] == tempstock:
227 | flag_repeat = 1
228 | # 用来标记是否新买入的股票
229 | g.matrix[n][9] = 1
230 | log.info(tempstock + "原本就有")
231 | break
232 | if not flag_repeat:
233 | # 不重复 就新加
234 | matrix2 = [0 for i in range(10)]
235 | matrix2[0] = tempstock
236 | # 用来标记是否新买入的股票
237 | matrix2[9] = 1
238 | matrix2[8] = tempstocks[1][num]
239 | matrix3 = copy.deepcopy(matrix2)
240 | g.matrix.append(matrix3)
241 | log.info(tempstock + "新买入")
242 | log.info(g.matrix)
243 | # 将这个月不合适的股票进行清仓
244 | # for num, stock in enumerate(g.matrix):
245 | # if stock[9] == 0:
246 | # sec = g.matrix[num][0]
247 | # order_target_value(sec, 0) # 清仓离场
248 | # g.record = g.record[g.record['symbol'] != sec] # 将卖出股票的记录清空
249 | # g.matrix.pop(num) # 将此股票从数组中删除
250 |
251 | # 用于计数
252 | g.means1 = g.means2 = g.means3 = g.means4 = g.means5 = g.means6 = 0
253 |
254 |
255 | # log.info(tempstocks)
256 |
257 | # 获得因子函数
258 | def get_needs(context, bar_dict, date, stocks):
259 | time_tuple = time.strptime(date, '%Y%m%d')
260 | year, month, day = time_tuple[:3]
261 | # 转化为datetime.date类型
262 | date = datetime.date(year, month, day)
263 |
264 | last_year_date = (date - relativedelta(years=1)).strftime('%Y%m%d')
265 | log.info('上个交易日前一年的日期为:' + last_year_date)
266 |
267 | # 上一年内的所有交易日期
268 | trade_days = get_trade_days(last_year_date, date).strftime('%Y-%m-%d')
269 |
270 | # 所有因子
271 | need_all = ','.join(context.need)
272 |
273 | # 1.分组单因子有效性检验,参考资料:https://www.ricequant.com/community/topic/702/%E5%8D%95%E4%B8%80%E5%9B%A0%E5%AD%90%E6%9C%89%E6%95%88%E6%80%A7%E6%A3%80%E6%B5%8B/2
274 | # 每隔25天(间隔越短,收益就会越随机)
275 | period = 25
276 |
277 | # 有效因子列表
278 | eff_need = []
279 |
280 | # 每组股票数10%只股票
281 | num = math.ceil(0.1 * len(stocks))
282 |
283 | # 获取开始日期因子值
284 | q = query(
285 | factor.symbol,
286 | need_all
287 | ).filter(
288 | factor.symbol.in_(stocks),
289 | # 20180430的数据消失,原因是:last_year_date可能不是交易日,这样就获取不到数据,因此为trade_days[0]
290 | factor.date == trade_days[0]
291 | )
292 |
293 | df = get_factors(q)
294 |
295 | for col in df.columns.values[1:]:
296 | # 分别按正序和倒序提取因子值前10%和后10%的股票
297 | a_hign = df.sort_values(col, axis=0, ascending=False)
298 | a_low = df.sort_values(col, axis=0, ascending=True)
299 |
300 | hign = a_hign.iloc[0:num, 0].values.tolist()
301 | low = a_low.ilo+c[0:num, 0].values.tolist()
302 | hign_price = get_price(hign, last_year_date, date, '1d', ['close'], skip_paused=False, fq='pre', is_panel=1)
303 | low_price = get_price(low, last_year_date, date, '1d', ['close'], skip_paused=False, fq='pre', is_panel=1)
304 |
305 | hign_price = hign_price['close']
306 | low_price = low_price['close']
307 |
308 | # 计算平均每组股票每日价格
309 | high_series = hign_price.T.mean()
310 | low_series = low_price.T.mean()
311 |
312 | # 计算收益率
313 | hign_rr = high_series.pct_change(period).dropna()
314 | low_rr = low_series.pct_change(period).dropna()
315 |
316 | # 判断两组数据的方差是否显著不同
317 | le = levene(hign_rr, low_rr)
318 |
319 | # 判断两组数据的均值是否显著不同
320 | tt = ttest_ind(hign_rr, low_rr, equal_var=False)
321 |
322 | # 取置信度为0.05
323 | if le.pvalue < 0.05 and tt.pvalue < 0.05:
324 | eff_need.append('factor.%s' % col)
325 |
326 | # 第一步有效因子个数
327 | # log.info(len(eff_need))
328 |
329 | effneed = ','.join(eff_need)
330 |
331 | # 第二步,取前50只股票来计算IC、IR、根据IC特征设定方向,根据每组IC均值设定因子权重(降低运算数量)
332 | q = query(
333 | factor.symbol,
334 | effneed
335 | ).filter(
336 | factor.symbol.in_(stocks),
337 | factor.date == trade_days[0]
338 | )
339 |
340 | df = get_factors(q)
341 |
342 | step = 50
343 | # 周期
344 | nd = 22
345 |
346 | # 用来存放各指标值
347 | need_data = {}
348 |
349 | # 循环每一个因子
350 | for col in df.columns.values[1:]:
351 |
352 | avr = [[], [], []]
353 | a_hign = df.sort_values(col, axis=0, ascending=False)
354 | sct = a_hign.iloc[0:step, 0].values.tolist()
355 |
356 | # 用来存放一组内每期ic值
357 | ics = []
358 |
359 | # 每个分期时间点
360 | days = trade_days[0:len(trade_days):nd]
361 |
362 | for i, rate in enumerate(g_rate(sct, days, nd)):
363 | q = query(
364 | col
365 | ).filter(
366 | factor.symbol.in_(sct),
367 | factor.date == days[i]
368 | )
369 |
370 | df_sct = get_factors(q).fillna(0)
371 |
372 | col = df_sct.columns.values[0]
373 | df_sct[col] = standardize(filter_extreme_3sigma(df_sct[col])).fillna(0)
374 |
375 | # ic值
376 | ic = pd.Series(df_sct[col]).corr(pd.Series(list(rate)))
377 |
378 | ics.append(ic)
379 |
380 | # 一组内三个指标
381 | avr_ic, ir, pr = ics_return(ics)
382 | avr[0].append(avr_ic)
383 | avr[1].append(ir)
384 | avr[2].append(pr)
385 |
386 | need_data[col] = {
387 | 'avr_ic': round(avr[0][0], 6),
388 | 'avr_ir': round(avr[1][0], 6),
389 | 'avr_pr': round(avr[2][0], 6)
390 | }
391 |
392 | # 对因子进行排序打分
393 | # 存放最终因子
394 | f_needs = {}
395 | log.info('未排序的因子字典:')
396 | log.info(need_data)
397 |
398 | data = pd.DataFrame(need_data).T.dropna()
399 |
400 | # 按照avr_ir降序打分
401 | data = data.sort_values('avr_ic', axis=0, ascending=False)
402 | data['avr_ic_score'] = list(reversed(range(len(data))))
403 |
404 | # 按照avr_ir降打分
405 | data = data.sort_values('avr_ir', axis=0, ascending=False)
406 | data['avr_ir_score'] = list(range(len(data)))
407 |
408 | # 分数相加
409 | data['total_socre'] = data['avr_ic_score'] + data['avr_ir_score']
410 | data = data.sort_values('total_socre', axis=0, ascending=False)
411 | data_stock = {col:data[col].tolist() for col in data.columns}
412 |
413 | # 获取排序后的因子
414 | fneeds = data._stat_axis.values.tolist()
415 | data_stock['factor'] = fneeds
416 | log.info('因子排序指标字典:')
417 | log.info(data_stock)
418 | if len(fneeds) >= context.need_max:
419 | fneeds = fneeds[0:context.need_max]
420 |
421 | # 获取因子权重
422 | for i in fneeds:
423 | weight = data.loc[i, 'avr_ic']
424 |
425 | if data.loc[i, 'avr_pr'] < 0.5:
426 | weight = - weight
427 |
428 | f_needs['factor.%s' % i] = {
429 | 'weight': weight
430 | }
431 | log.info(f_needs)
432 | return f_needs
433 |
434 |
435 | # 获得股票函数
436 | def get_stocks(context, bar_dict, needs, date, stocks):
437 | # needs为如下字典:
438 | # 例{'factor.dbcd': {'weight': -0.146216}, 'factor.weighted_roe': {'weight': 0.108531}, 'factor.turnover_of_current_assets': {'weight': -0.111679}, 'factor.turnover_of_overall_assets': {'weight': -0.148806}, 'factor.administration_cost_div_income': {'weight': 0.118929}}
439 | need_all = ','.join(list(needs.keys()))
440 | q = query(
441 | factor.symbol,
442 | need_all
443 | ).filter(
444 | factor.symbol.in_(stocks),
445 | factor.date == date
446 | )
447 | df = get_factors(q).fillna(0)
448 |
449 | # 用来存放股票分值
450 | score = []
451 |
452 | weight = [needs[i]['weight'] for i in needs]
453 |
454 | for i in range(len(df)):
455 | result = sum(list(map(lambda x, y: x * y, list(df.iloc[i, 1:]), weight)))
456 | score.append(result)
457 |
458 | df['score'] = score
459 |
460 | df = df.sort_values('score', axis=0, ascending=False)
461 | df_stock = {col:df[col].tolist() for col in df.columns}
462 |
463 | log.info('股票排序字典:')
464 | log.info(df_stock)
465 | # 大小盘默认持股数
466 | hold = int(context.hold_max / 2)
467 |
468 | # 存放选取的股票
469 | stocks_c = list(df.iloc[0: hold, 0])
470 |
471 | return stocks_c
472 |
473 |
474 | # 每日运行函数
475 | def handle_bar(context, bar_dict):
476 | last_date = get_last_datetime().strftime('%Y%m%d')
477 | trade_before_new(context, bar_dict)
478 | trade_new(context, bar_dict)
479 | log.info(
480 | "VOL5上穿VOL10次数:{},OL5下穿VOL10:{},DIF{},MA5上穿MA10:{},MA5下穿MA10:{},布林线:{}".format(g.means1, g.means2, g.means3,
481 | g.means4, g.means5, g.means6))
482 |
483 |
484 | # log.info('上个交易日日期为:' + last_date)
485 | def trade_new(context, bar_dict):
486 | a = get_datetime()
487 | # current_universe = context.iwencai_securities # 获取当前除停牌外的所有可供投资股票(universe)
488 |
489 | # security_position = context.portfolio.stock_account.positions.keys() # 字典数据,上一K线结束后的有效证券头寸,即持仓数量大于0的证券及其头寸
490 | for index, jj in enumerate(g.matrix):
491 |
492 | stock = g.matrix[index][0] # 获得备选列表中的股票代码
493 | sec = stock
494 | curent = get_candle_stick([stock], end_date=a, fre_step='1m', fields=['open', 'close', 'high', 'low', 'volume'],
495 | skip_paused=False, fq=None, bar_count=5, is_panel=0)
496 | current_price = curent[stock]['open'][-1]
497 |
498 | value = history([stock], ['close', 'open', 'low', 'turnover', 'high'], 20, '1d', False, None)
499 |
500 | value1 = history(stock, ['close', 'open', 'low', 'volume', 'high'], 60, '1d', True, 'pre')
501 | value1 = value1.dropna()
502 | close = value1.close.values
503 | # log.info(close, value1, stock)
504 | low = value1.low.values
505 | # high = value1['high'].as_matrix()
506 | high = value1.high.values
507 | vol = value1['volume'].as_matrix()
508 | current_price = close[-1] # 获得当前时刻价格
509 | # short_ma = ta.MA(close, 5) # 5天均线
510 | # long_ma = ta.MA(close, 10) # 10天均线
511 | # mid_ma = ta.MA(close, 20) # 20天均线
512 | # up, mid,down = talib.BBANDS(close, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0)
513 | # print(up)
514 | # 数据处理(策略部分):计算真实波幅,
515 | try:
516 | atr = talib.ATR(high, low, close, 20)[-1] # 计算真实波幅
517 | except:
518 | print(high, low, close)
519 |
520 | upperband, middleband, lowerband = talib.BBANDS(np.asarray(value[stock]['close']), timeperiod=10, nbdevup=1.96,
521 | nbdevdn=1.96, matype=0)
522 | # log.info(g.matrix[index][0],g.matrix[index][1],g.matrix[index][3],g.matrix[index][4])
523 | if g.matrix[index][8] == 1:
524 | flag_buy = 0
525 | for t in g.record['symbol']:
526 | if t == sec:
527 | flag_buy = 1
528 | if (g.record[g.record['symbol'] == sec]['last_buy_price'].empty):
529 | continue
530 | else:
531 |
532 | last_price = float(g.record[g.record['symbol'] == sec]['last_buy_price']) # 上一次的买入价格(查找、判断、删除)
533 | add_price = last_price + 0.5 * atr # 计算是否加仓的判断价格
534 | add_price_top = last_price + 1.5 * atr
535 | add_unit = float(g.record[g.record['symbol'] == sec]['add_time']) # 已加仓次数
536 | # if current_price > add_price and add_unit < 4 and g.matrix[index][9]==1: # 价格上涨超过0.5N并且加仓次数小于4次
537 | if current_price > add_price and g.matrix[index][9] == 1: # 价格上涨超过0.5N并且加仓次数小于4次
538 | log.info("加仓")
539 | log.info(sec)
540 | unit = calcUnit(context.portfolio.available_cash, atr) # 计算加仓时应购入的股票数量
541 | log.info(unit)
542 | try:
543 | if g.record[g.record['symbol'] == sec]['add_time'] == 0:
544 | order(sec, 1 * unit) # 买入2unit的股票
545 | log.info("买入2unit的股票")
546 | elif g.record[g.record['symbol'] == sec]['add_time'] == 1:
547 | order(sec, 1 * unit) # 买入1.5unit的股票
548 | log.info("买入1.5unit的股票")
549 | elif g.record[g.record['symbol'] == sec]['add_time'] == 2:
550 | order(sec, 1 * unit) # 买入1unit的股票
551 | log.info("买入1unit的股票")
552 | elif g.record[g.record['symbol'] == sec]['add_time'] == 3:
553 | order(sec, 1 * unit) # 买入1unit的股票
554 | log.info("买入0.5unit的股票")
555 | except:
556 | order(sec, 1 * unit) # 买入2unit的股票
557 | log.info("买入2unit的股票")
558 | g.record.loc[g.record['symbol'] == sec, 'add_time'] = g.record[g.record['symbol'] == sec][
559 | 'add_time'] + 1 # 加仓次数+1(先找到值再找到位置,然后赋值)
560 | g.record.loc[g.record['symbol'] == sec, 'last_buy_price'] = current_price # 加仓次数+1
561 | # 策略部分(离场:止损或止盈):当前价格下穿到BOLL上轨或相对上个买入价下跌 2ATR时(止损)或当股价5日均线下穿10日均线并且量均线死叉,清仓离场
562 | if current_price < (last_price - 2 * atr) or current_price < low[-10:-1].min():
563 | log.info("抛售")
564 | log.info(sec)
565 | g.means7 += 1
566 | order_target_value(sec, 0) # 清仓离场
567 |
568 | g.record = g.record[g.record['symbol'] != sec] # 将卖出股票的记录清空
569 |
570 | if ((g.matrix[index][1] == True and g.matrix[index][3] == True and g.matrix[index][4] == True) or (
571 | g.matrix[index][6] == True and flag_buy == 0)) and g.matrix[index][9] == 1:
572 | g.num += 1
573 | order_target_value(stock, context.portfolio.available_cash / 40)
574 | log.info("购入大盘")
575 | if len(g.record) != 0:
576 | g.record = g.record[
577 | g.record[
578 | 'symbol'] != stock] # 清空g.record中sec过期的记录,因为如果之前记录就有000001,现在将这个股票建仓,就要先删除原来为symbol的记录。
579 |
580 | g.record = g.record.append(pd.DataFrame(
581 | {'symbol': [stock], 'add_time': [1], 'last_buy_price': [current_price]})) # 记录股票,加仓次数及买入价格
582 | continue
583 |
584 | if g.matrix[index][8] == 0:
585 | flag_buy = 0
586 | for t in g.record['symbol']:
587 | if t == sec:
588 | flag_buy = 1
589 | if (g.record[g.record['symbol'] == sec]['last_buy_price'].empty):
590 | continue
591 | else:
592 |
593 | last_price = float(g.record[g.record['symbol'] == sec]['last_buy_price']) # 上一次的买入价格(查找、判断、删除)
594 | add_price = last_price + 0.5 * atr # 计算是否加仓的判断价格
595 | add_price_top = last_price + 1.5 * atr
596 | add_unit = float(g.record[g.record['symbol'] == sec]['add_time']) # 已加仓次数
597 | # if current_price > add_price and add_unit < 4 and current_price >= (
598 | # last_price - 2 * atr)and g.matrix[index][9]==1: # 价格上涨超过0.5N并且加仓次数小于4次
599 | if current_price > add_price and current_price >= (
600 | last_price - 2 * atr) and g.matrix[index][9] == 1: # 价格上涨超过0.5N并且加仓次数小于4次
601 | log.info("加仓")
602 | log.info(sec)
603 | unit = calcUnit(context.portfolio.available_cash, atr) # 计算加仓时应购入的股票数量
604 | log.info(unit)
605 | try:
606 | if g.record[g.record['symbol'] == sec]['add_time'] == 0:
607 | order(sec, 1 * unit) # 买入1unit的股票
608 | elif g.record[g.record['symbol'] == sec]['add_time'] == 1:
609 | order(sec, 1 * unit) # 买入1unit的股票
610 | elif g.record[g.record['symbol'] == sec]['add_time'] == 2:
611 | order(sec, 1 * unit) # 买入1unit的股票
612 | elif g.record[g.record['symbol'] == sec]['add_time'] == 3:
613 | order(sec, 1 * unit) # 买入1unit的股票
614 | except:
615 | order(sec, 1 * unit) # 买入1unit的股票
616 | g.record.loc[g.record['symbol'] == sec, 'add_time'] = g.record[g.record['symbol'] == sec][
617 | 'add_time'] + 1 # 加仓次数+1(先找到值再找到位置,然后赋值)
618 | g.record.loc[g.record['symbol'] == sec, 'last_buy_price'] = current_price # 加仓次数+1
619 | # 策略部分(离场:止损或止盈):当前价格下穿到BOLL上轨或相对上个买入价下跌 2ATR时(止损)或当股价5日均线下穿10日均线并且量均线死叉,清仓离场
620 | if current_price < (last_price - 2 * atr) or current_price < low[-10:-1].min():
621 | log.info("抛售")
622 | g.means7 += 1
623 | log.info(sec)
624 | order_target_value(sec, 0) # 清仓离场
625 |
626 | g.record = g.record[g.record['symbol'] != sec] # 将卖出股票的记录清空
627 |
628 | if ((g.matrix[index][1] == True and g.matrix[index][3] == True and g.matrix[index][4] == True) or (
629 | g.matrix[index][6] == False) and (flag_buy == 0)) and g.matrix[index][9] == 1:
630 |
631 | log.info("购入小盘")
632 | g.num += 1
633 | # log.info(stock)
634 | # log.info('g.matrix[index][4]==True and g.matrix[index][6]==True and g.matrix[index][2]==True')
635 | order_target_value(stock, context.portfolio.available_cash / 40)
636 | if len(g.record) != 0:
637 | g.record = g.record[
638 | g.record[
639 | 'symbol'] != stock] # 清空g.record中sec过期的记录,因为如果之前记录就有000001,现在将这个股票建仓,就要先删除原来为symbol的记录。
640 |
641 | g.record = g.record.append(pd.DataFrame(
642 | {'symbol': [stock], 'add_time': [1], 'last_buy_price': [current_price]})) # 记录股票,加仓次数及买入价格
643 | continue
644 |
645 |
646 | def trade_before_new(context, bar_dict):
647 | if g.matrix[0][0] == 0:
648 | reallocate(context, bar_dict)
649 | # log.info(g.matrix)
650 | a = get_datetime()
651 | xiaopan = '000905.SH'
652 | dapan = '000300.SH'
653 | Indexprice_xiao = get_price(xiaopan, None, get_datetime().strftime("%Y%m%d"), '1d', ['close'], True, None, 100, is_panel=1)
654 | Indexprice_da = get_price(dapan, None, get_datetime().strftime("%Y%m%d"), '1d', ['close'], True, None, 100, is_panel=1)
655 | strong_weak = np.log(Indexprice_xiao.close) - np.log(Indexprice_da.close)
656 | strong_weak = strong_weak[20:]
657 | lowerband_new = pd.rolling_mean(strong_weak, 20) - 2 * pd.rolling_std(strong_weak, 20)
658 | up_new = pd.rolling_mean(strong_weak, 20) + 2 * pd.rolling_std(strong_weak, 20)
659 | for index, jj in enumerate(g.matrix):
660 |
661 | # 基本数据获取
662 | stock = g.matrix[index][0] # 获得备选列表中的股票代码
663 | # log.info(g.matrix[index])
664 | sec = stock
665 | curent = get_candle_stick([stock], end_date=a, fre_step='1m', fields=['open', 'close', 'high', 'low', 'volume'],
666 | skip_paused=False, fq=None, bar_count=5, is_panel=0)
667 | value = history([stock], ['close', 'open', 'low', 'turnover', 'high'], 20, '1d', False, None)
668 |
669 | value1 = history(stock, ['close', 'open', 'low', 'volume', 'high'], 110, '1d', True, 'pre')
670 | value1 = value1.dropna()
671 | close = value1.close.values
672 | low = value1.low.values
673 | high = value1['high'].as_matrix()
674 | vol = value1['volume'].as_matrix()
675 |
676 | # current_price = close[-1] # 获得当前时刻价格
677 | # log.info(close)
678 | short_ma = ta.MA(close, 5) # 5天均线
679 | long_ma = ta.MA(close, 10) # 10天均线
680 | mid_ma = ta.MA(close, 20) # 20天均线
681 | up, mid, low = talib.BBANDS(close, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0)
682 | # 数据处理(策略部分):计算真实波幅,
683 | # atr = talib.ATR(high, low, close, 20)[-1] # 计算真实波幅
684 |
685 | # 第一部分 计算#VOL5上穿VOL10
686 |
687 | short_vol = ta.MA(vol, 5) # 过去5天均量线
688 | long_vol = ta.MA(vol, 10) # 过去10天均量线
689 |
690 | if short_vol[-1] > short_vol[-2] and long_vol[-1] > long_vol[-2] and short_vol[-2] <= long_vol[-2] and \
691 | short_vol[-1] > long_vol[-1]:
692 | g.matrix[index][1] = True # VOL5上穿VOL10且多头向上排列,可建仓标志
693 | g.means1 += 1
694 | else:
695 | g.matrix[index][1] = False # 不可建仓标志
696 |
697 | if (short_vol[-2] >= long_vol[-2] and short_vol[-1] < long_vol[-1]):
698 | g.matrix[index][2] = True # VOL5下穿VOL10,可清仓标志
699 | g.means2 += 1
700 | else:
701 | g.matrix[index][2] = False # 不可清仓标志
702 |
703 | # 第二部分 #MACD中的DIF>DEA & DIF>0 & DEA>0
704 |
705 | close_macd = value[stock]['close'].as_matrix()
706 |
707 | DIF, DEA, MACD = talib.MACD(close, fastperiod=12, slowperiod=26,
708 | signalperiod=9) # talib提供的MACD计算函数,计算DIF,DEF以及MACD的取值
709 |
710 | if DIF[-1] > DEA[-1] and DIF[-1] > 0 and DEA[-1] > 0:
711 | g.matrix[index][3] = True
712 | g.means3 += 1
713 |
714 | else:
715 | g.matrix[index][3] = False
716 |
717 | # 第三部分 是否触发MA5与MA10建仓或离场标志
718 |
719 | if short_ma[-1] > mid_ma[-2] and short_ma[-1] > short_ma[-2] and mid_ma[-1] > mid_ma[-2] and short_ma[-1] > \
720 | mid_ma[-1] and mid_ma[-1] > long_ma[-1] and short_ma[-2] <= mid_ma[-2] and short_ma[-1] > mid_ma[-1]:
721 | g.matrix[index][4] = True # MA5上穿MA10且三线多头向上排列,可建仓标志
722 | g.means4 += 1
723 | else:
724 | g.matrix[index][4] = False # 不可建仓标志
725 | if (short_ma[-2] >= mid_ma[-2] and short_ma[-1] < mid_ma[-1]):
726 | g.matrix[index][5] = True # MA5下穿MA10或者当前价跌破MA10,可清仓标志
727 | g.means5 += 1
728 | else:
729 | g.matrix[index][5] = False # 不可清仓标志
730 |
731 | # 第四部分 创新穿越次数
732 |
733 | # value_new = history([stock], ['close', 'open', 'low', 'turnover', 'high'], 110, '1d', False, None)
734 | # upperband_new, middleband_new, lowerband_new = talib.BBANDS(np.asarray(value_new[stock]['close']),
735 | # timeperiod=10, nbdevup=1.96, nbdevdn=1.96, matype=0)
736 | flag_shangchuan = 0
737 | flag_xiachuan = 0
738 | flag_shangchuancishu = 0
739 | flag_xiachuancishu = 0
740 |
741 | # for t in range(9, 109):
742 | # if flag_shangchuan == 0:
743 | # if strong_weak[t] < upperband_new[t]:
744 | # # 定义标识数
745 | # flag_shangchuan = 1
746 | # if flag_shangchuan == 1:
747 | # if strong_weak[t] > upperband_new[t]:
748 | # flag_shangchuancishu = flag_shangchuancishu + 1
749 | # flag_shangchuan = 2
750 | # if flag_shangchuan == 2:
751 | # if strong_weak[t] > upperband_new[t]:
752 | # flag_shangchuan = 2
753 | # if strong_weak[t] < upperband_new[t]:
754 | # flag_shangchuan = 0
755 | if (strong_weak[-2] > lowerband_new[-2]) and (strong_weak[-1] < lowerband_new[-1]):
756 | # log.info(strong_weak[-2],lowerband_new[-2])
757 | # log.info(strong_weak[-1],lowerband_new[-1])
758 | g.matrix[index][6] = True
759 | g.means6 += 1
760 | else:
761 | g.matrix[index][6] = False
762 | flag_xiachuancishu = 0
763 | # for t in range(1, 80):
764 | # if flag_xiachuan == 0:
765 | # if strong_weak[t] > lowerband_new[t]:
766 | # # 定义标识数
767 | # flag_xiachuan = 1
768 | # if flag_xiachuan == 1:
769 | # if strong_weak[t] <= lowerband_new[t]:
770 | # flag_xiachuancishu = 1
771 | # flag_xiachuan = 2
772 | #
773 | # if flag_xiachuancishu != 0:
774 | # # log.info("!=0")
775 | # # log.info(stock+"触碰布林线下穿线")
776 | # g.matrix[index][6] = True
777 | # g.means6+=1
778 | # else:
779 | # # log.info("==0")
780 | # # log.info(stock + "不触碰布林线下穿线")
781 | # g.matrix[index][6] = False
782 |
783 | # trade(context)
784 |
785 |
786 | # #收盘上穿20日BOLL下轨
787 |
788 |
789 | # 日均线穿布林线策略。该函数返回0 1 2 三类数据。0代表 上穿次数大于下 1代表相等 2代表 上穿小于下穿
790 |
791 | # #加仓条件
792 |
793 | def calcUnit(portfolio_value, ATR):
794 | value = portfolio_value * 0.01 # trade_percent
795 | return int((value / ATR) / 100) * 100
796 |
797 |
798 | # 1. 获取上月月末日期
799 | def func_get_end_date_of_last_month(current_date):
800 | # 获取从上一个交易日前一个月中的所有交易日,日期排序从前至后
801 | trade_days = list(get_trade_days(None, current_date, count=30))
802 |
803 | # 转化为%Y%m%d格式
804 | for i in range(len(trade_days)):
805 | trade_days[i] = trade_days[i].strftime('%Y%m%d')
806 |
807 | # 只要交易日的date和当前交易日的月份不同即为上一个月月末日期,例如[20171013]-[20170928]
808 | # reversed反转序列,便于快速找到月末日期
809 | for date in reversed(trade_days):
810 | if date[5] != current_date[5]:
811 | return date
812 |
813 | log.info('找不到上个月末调仓日!')
814 | return
815 |
816 |
817 | # 获取收益率序列
818 | def g_rate(sct, days, nd):
819 | for i, day in enumerate(days):
820 | if i >= 1:
821 | prices = \
822 | get_price(sct, days[i - 1], days[i], '%dd' % nd, ['close'], skip_paused=False, fq='pre', is_panel=1)[
823 | 'close']
824 | close_rate = (prices.iloc[-1] - prices.iloc[0]) / prices.iloc[0]
825 | yield close_rate
826 |
827 |
828 | # 计算一组数据的IC绝对值均值、正向比例、均值/标准差
829 | def ics_return(l):
830 | # 信息系数绝对值均值
831 | a = [abs(i) for i in l]
832 | avr_ic = sum(a) / len(l)
833 |
834 | # 信息比率
835 | b = np.array(l)
836 | if b.std() != 0:
837 | ir = b.mean() / b.std()
838 | else:
839 | ir = 0
840 |
841 | # 正向比例
842 | c = [i for i in l if i > 0]
843 | pr = len(c) / len(l)
844 | return avr_ic, ir, pr
845 |
846 |
847 | # 求一组数的均值并保留6位小数
848 | def avr_6(l):
849 | b = round(np.array(l).mean(), 6)
850 | return b
851 |
852 |
853 | # 3 sigma 去极值
854 | def filter_extreme_3sigma(series, n=3):
855 | # 均值
856 | mean = series.mean()
857 |
858 | # 标准差
859 | std = series.std()
860 |
861 | max_range = mean + n * std
862 | min_range = mean - n * std
863 |
864 | # clip函数用于将超出范围的值填充为min_range,max_range
865 | return np.clip(series, min_range, max_range)
866 |
867 |
868 | # z-score标准化
869 | def standardize(series):
870 | std = series.std()
871 | mean = series.mean()
872 |
873 | return (series - mean) / std
874 |
875 |
876 | # 随机迭代器
877 | def gp_random(stocks, gp, num):
878 | for i in range(gp):
879 | yield random.sample(stocks, num)
880 |
881 |
--------------------------------------------------------------------------------